mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-17 22:14:26 +01:00
Can start without NBXplorer being ready
This commit is contained in:
@@ -7,9 +7,9 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0-preview-20170720-02" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.5.0" />
|
||||||
<PackageReference Include="xunit" Version="2.3.1" />
|
<PackageReference Include="xunit" Version="2.3.1" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -106,19 +106,21 @@ namespace BTCPayServer.Tests
|
|||||||
.UseStartup<Startup>()
|
.UseStartup<Startup>()
|
||||||
.Build();
|
.Build();
|
||||||
_Host.Start();
|
_Host.Start();
|
||||||
Runtime = (BTCPayServerRuntime)_Host.Services.GetService(typeof(BTCPayServerRuntime));
|
InvoiceRepository = (InvoiceRepository)_Host.Services.GetService(typeof(InvoiceRepository));
|
||||||
var watcher = (InvoiceWatcher)_Host.Services.GetService(typeof(InvoiceWatcher));
|
|
||||||
}
|
|
||||||
|
|
||||||
public BTCPayServerRuntime Runtime
|
var waiter = ((NBXplorerWaiterAccessor)_Host.Services.GetService(typeof(NBXplorerWaiterAccessor))).Instance;
|
||||||
{
|
while(waiter.State != NBXplorerState.Ready)
|
||||||
get; set;
|
{
|
||||||
|
Thread.Sleep(10);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string HostName
|
public string HostName
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
internal set;
|
internal set;
|
||||||
}
|
}
|
||||||
|
public InvoiceRepository InvoiceRepository { get; private set; }
|
||||||
|
|
||||||
public T GetService<T>()
|
public T GetService<T>()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -307,13 +307,13 @@ namespace BTCPayServer.Tests
|
|||||||
|
|
||||||
Eventually(() =>
|
Eventually(() =>
|
||||||
{
|
{
|
||||||
var textSearchResult = tester.PayTester.Runtime.InvoiceRepository.GetInvoices(new InvoiceQuery()
|
var textSearchResult = tester.PayTester.InvoiceRepository.GetInvoices(new InvoiceQuery()
|
||||||
{
|
{
|
||||||
StoreId = user.StoreId,
|
StoreId = user.StoreId,
|
||||||
TextSearch = invoice.OrderId
|
TextSearch = invoice.OrderId
|
||||||
}).GetAwaiter().GetResult();
|
}).GetAwaiter().GetResult();
|
||||||
Assert.Equal(1, textSearchResult.Length);
|
Assert.Equal(1, textSearchResult.Length);
|
||||||
textSearchResult = tester.PayTester.Runtime.InvoiceRepository.GetInvoices(new InvoiceQuery()
|
textSearchResult = tester.PayTester.InvoiceRepository.GetInvoices(new InvoiceQuery()
|
||||||
{
|
{
|
||||||
StoreId = user.StoreId,
|
StoreId = user.StoreId,
|
||||||
TextSearch = invoice.Id
|
TextSearch = invoice.Id
|
||||||
|
|||||||
@@ -13,12 +13,15 @@ services:
|
|||||||
TESTS_RPCCONNECTION: server=http://bitcoind:43782;ceiwHEbqWI83:DwubwWsoo3
|
TESTS_RPCCONNECTION: server=http://bitcoind:43782;ceiwHEbqWI83:DwubwWsoo3
|
||||||
TESTS_NBXPLORERURL: http://nbxplorer:32838/
|
TESTS_NBXPLORERURL: http://nbxplorer:32838/
|
||||||
TESTS_POSTGRES: User ID=postgres;Host=postgres;Port=5432;Database=btcpayserver
|
TESTS_POSTGRES: User ID=postgres;Host=postgres;Port=5432;Database=btcpayserver
|
||||||
TESTS_FAKECALLBACK: 'true'
|
TESTS_FAKECALLBACK: 'false'
|
||||||
TESTS_PORT: 80
|
TESTS_PORT: 80
|
||||||
TESTS_HOSTNAME: tests
|
TESTS_HOSTNAME: tests
|
||||||
|
TEST_ECLAIR1: http://eclair1:8080/
|
||||||
|
TEST_ECLAIR2: http://eclair2:8080/
|
||||||
expose:
|
expose:
|
||||||
- "80"
|
- "80"
|
||||||
links:
|
links:
|
||||||
|
- bitcoind
|
||||||
- nbxplorer
|
- nbxplorer
|
||||||
- eclair1
|
- eclair1
|
||||||
- eclair2
|
- eclair2
|
||||||
@@ -39,7 +42,7 @@ services:
|
|||||||
- eclair2
|
- eclair2
|
||||||
|
|
||||||
nbxplorer:
|
nbxplorer:
|
||||||
image: nicolasdorier/nbxplorer:1.0.0.29
|
image: nicolasdorier/nbxplorer:1.0.0.32
|
||||||
ports:
|
ports:
|
||||||
- "32838:32838"
|
- "32838:32838"
|
||||||
expose:
|
expose:
|
||||||
|
|||||||
@@ -18,13 +18,13 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Hangfire" Version="1.6.17" />
|
<PackageReference Include="Hangfire" Version="1.6.17" />
|
||||||
<PackageReference Include="Hangfire.MemoryStorage" Version="1.5.1" />
|
<PackageReference Include="Hangfire.MemoryStorage" Version="1.5.2" />
|
||||||
<PackageReference Include="Hangfire.PostgreSql" Version="1.4.8.1" />
|
<PackageReference Include="Hangfire.PostgreSql" Version="1.4.8.1" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Filter" Version="1.1.2" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Filter" Version="1.1.2" />
|
||||||
<PackageReference Include="NBitcoin" Version="4.0.0.48" />
|
<PackageReference Include="NBitcoin" Version="4.0.0.50" />
|
||||||
<PackageReference Include="NBitpayClient" Version="1.0.0.13" />
|
<PackageReference Include="NBitpayClient" Version="1.0.0.13" />
|
||||||
<PackageReference Include="DBreeze" Version="1.87.0" />
|
<PackageReference Include="DBreeze" Version="1.87.0" />
|
||||||
<PackageReference Include="NBXplorer.Client" Version="1.0.0.18" />
|
<PackageReference Include="NBXplorer.Client" Version="1.0.0.20" />
|
||||||
<PackageReference Include="NicolasDorier.CommandLine" Version="1.0.0.1" />
|
<PackageReference Include="NicolasDorier.CommandLine" Version="1.0.0.1" />
|
||||||
<PackageReference Include="NicolasDorier.CommandLine.Configuration" Version="1.0.0.2" />
|
<PackageReference Include="NicolasDorier.CommandLine.Configuration" Version="1.0.0.2" />
|
||||||
<PackageReference Include="NicolasDorier.StandardConfiguration" Version="1.0.0.13" />
|
<PackageReference Include="NicolasDorier.StandardConfiguration" Version="1.0.0.13" />
|
||||||
@@ -34,9 +34,9 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
|
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.3" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.0.0" PrivateAssets="All" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.0.1" PrivateAssets="All" />
|
||||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.0.0" PrivateAssets="All" />
|
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.0.1" PrivateAssets="All" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -55,14 +55,9 @@ namespace BTCPayServer.Configuration
|
|||||||
|
|
||||||
Explorer = conf.GetOrDefault<Uri>("explorer.url", networkInfo.DefaultExplorerUrl);
|
Explorer = conf.GetOrDefault<Uri>("explorer.url", networkInfo.DefaultExplorerUrl);
|
||||||
CookieFile = conf.GetOrDefault<string>("explorer.cookiefile", networkInfo.DefaultExplorerCookieFile);
|
CookieFile = conf.GetOrDefault<string>("explorer.cookiefile", networkInfo.DefaultExplorerCookieFile);
|
||||||
RequireHttps = conf.GetOrDefault<bool>("requirehttps", false);
|
|
||||||
PostgresConnectionString = conf.GetOrDefault<string>("postgres", null);
|
PostgresConnectionString = conf.GetOrDefault<string>("postgres", null);
|
||||||
ExternalUrl = conf.GetOrDefault<Uri>("externalurl", null);
|
ExternalUrl = conf.GetOrDefault<Uri>("externalurl", null);
|
||||||
}
|
InternalUrl = conf.GetOrDefault<Uri>("internalurl", null);
|
||||||
|
|
||||||
public bool RequireHttps
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
}
|
||||||
public string PostgresConnectionString
|
public string PostgresConnectionString
|
||||||
{
|
{
|
||||||
@@ -74,5 +69,6 @@ namespace BTCPayServer.Configuration
|
|||||||
get;
|
get;
|
||||||
set;
|
set;
|
||||||
}
|
}
|
||||||
|
public Uri InternalUrl { get; private set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,110 +0,0 @@
|
|||||||
using BTCPayServer.Authentication;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using BTCPayServer.Logging;
|
|
||||||
using DBreeze;
|
|
||||||
using NBitcoin;
|
|
||||||
using NBXplorer;
|
|
||||||
using NBXplorer.DerivationStrategy;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using BTCPayServer.Data;
|
|
||||||
using BTCPayServer.Services.Invoices;
|
|
||||||
using BTCPayServer.Services.Wallets;
|
|
||||||
|
|
||||||
namespace BTCPayServer.Configuration
|
|
||||||
{
|
|
||||||
public class BTCPayServerRuntime : IDisposable
|
|
||||||
{
|
|
||||||
public ExplorerClient Explorer
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
private set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Configure(BTCPayServerOptions opts)
|
|
||||||
{
|
|
||||||
ConfigureAsync(opts).GetAwaiter().GetResult();
|
|
||||||
}
|
|
||||||
public async Task ConfigureAsync(BTCPayServerOptions opts)
|
|
||||||
{
|
|
||||||
Network = opts.Network;
|
|
||||||
Explorer = new ExplorerClient(opts.Network, opts.Explorer);
|
|
||||||
|
|
||||||
if (!Explorer.SetCookieAuth(opts.CookieFile))
|
|
||||||
Explorer.SetNoAuth();
|
|
||||||
|
|
||||||
CancellationTokenSource cts = new CancellationTokenSource(30000);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Logs.Configuration.LogInformation("Trying to connect to explorer " + Explorer.Address.AbsoluteUri);
|
|
||||||
await Explorer.WaitServerStartedAsync(cts.Token).ConfigureAwait(false);
|
|
||||||
Logs.Configuration.LogInformation("Connection successfull");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
throw new ConfigException($"Could not connect to NBXplorer, {ex.Message}");
|
|
||||||
}
|
|
||||||
|
|
||||||
ApplicationDbContextFactory dbContext = null;
|
|
||||||
if (opts.PostgresConnectionString == null)
|
|
||||||
{
|
|
||||||
var connStr = "Data Source=" + Path.Combine(opts.DataDir, "sqllite.db");
|
|
||||||
Logs.Configuration.LogInformation($"SQLite DB used ({connStr})");
|
|
||||||
dbContext = new ApplicationDbContextFactory(DatabaseType.Sqlite, connStr);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Logs.Configuration.LogInformation($"Postgres DB used ({opts.PostgresConnectionString})");
|
|
||||||
dbContext = new ApplicationDbContextFactory(DatabaseType.Postgres, opts.PostgresConnectionString);
|
|
||||||
}
|
|
||||||
DBFactory = dbContext;
|
|
||||||
|
|
||||||
InvoiceRepository = new InvoiceRepository(dbContext, CreateDBPath(opts, "InvoiceDB"), Network);
|
|
||||||
_Resources.Add(InvoiceRepository);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string CreateDBPath(BTCPayServerOptions opts, string name)
|
|
||||||
{
|
|
||||||
var dbpath = Path.Combine(opts.DataDir, name);
|
|
||||||
if (!Directory.Exists(dbpath))
|
|
||||||
Directory.CreateDirectory(dbpath);
|
|
||||||
return dbpath;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<IDisposable> _Resources = new List<IDisposable>();
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
lock (_Resources)
|
|
||||||
{
|
|
||||||
foreach (var r in _Resources)
|
|
||||||
{
|
|
||||||
r.Dispose();
|
|
||||||
}
|
|
||||||
_Resources.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Network Network
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
private set;
|
|
||||||
}
|
|
||||||
public InvoiceRepository InvoiceRepository
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
set;
|
|
||||||
}
|
|
||||||
public ApplicationDbContextFactory DBFactory
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
set;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -26,11 +26,11 @@ namespace BTCPayServer.Configuration
|
|||||||
app.Option("-n | --network", $"Set the network among ({NetworkInformation.ToStringAll()}) (default: {Network.Main.ToString()})", CommandOptionType.SingleValue);
|
app.Option("-n | --network", $"Set the network among ({NetworkInformation.ToStringAll()}) (default: {Network.Main.ToString()})", CommandOptionType.SingleValue);
|
||||||
app.Option("--testnet | -testnet", $"Use testnet", CommandOptionType.BoolValue);
|
app.Option("--testnet | -testnet", $"Use testnet", CommandOptionType.BoolValue);
|
||||||
app.Option("--regtest | -regtest", $"Use regtest", CommandOptionType.BoolValue);
|
app.Option("--regtest | -regtest", $"Use regtest", CommandOptionType.BoolValue);
|
||||||
app.Option("--requirehttps", $"Will redirect to https version of the website (default: false)", CommandOptionType.BoolValue);
|
|
||||||
app.Option("--postgres", $"Connection string to postgres database (default: sqlite is used)", CommandOptionType.SingleValue);
|
app.Option("--postgres", $"Connection string to postgres database (default: sqlite is used)", CommandOptionType.SingleValue);
|
||||||
app.Option("--explorerurl", $"Url of the NBxplorer (default: : Default setting of NBXplorer for the network)", CommandOptionType.SingleValue);
|
app.Option("--explorerurl", $"Url of the NBxplorer (default: : Default setting of NBXplorer for the network)", CommandOptionType.SingleValue);
|
||||||
app.Option("--explorercookiefile", $"Path to the cookie file (default: Default setting of NBXplorer for the network)", CommandOptionType.SingleValue);
|
app.Option("--explorercookiefile", $"Path to the cookie file (default: Default setting of NBXplorer for the network)", CommandOptionType.SingleValue);
|
||||||
app.Option("--externalurl", $"The expected external url of this service, use if BTCPay is behind a reverse proxy (default: empty, use the incoming HTTP request to figure out)", CommandOptionType.SingleValue);
|
app.Option("--externalurl", $"The expected external url of this service, to use if BTCPay is behind a reverse proxy (default: empty, use the incoming HTTP request to figure out)", CommandOptionType.SingleValue);
|
||||||
|
app.Option("--internalurl", $"The expected internal url of this service, this set NBXplorer callback addresses (default: empty, use the incoming HTTP request to figure out)", CommandOptionType.SingleValue);
|
||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,7 +77,6 @@ namespace BTCPayServer.Configuration
|
|||||||
builder.AppendLine("#regtest=0");
|
builder.AppendLine("#regtest=0");
|
||||||
builder.AppendLine();
|
builder.AppendLine();
|
||||||
builder.AppendLine("### Server settings ###");
|
builder.AppendLine("### Server settings ###");
|
||||||
builder.AppendLine("#requirehttps=0");
|
|
||||||
builder.AppendLine("#port=" + network.DefaultPort);
|
builder.AppendLine("#port=" + network.DefaultPort);
|
||||||
builder.AppendLine("#bind=127.0.0.1");
|
builder.AppendLine("#bind=127.0.0.1");
|
||||||
builder.AppendLine();
|
builder.AppendLine();
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using BTCPayServer.Configuration;
|
||||||
|
|
||||||
namespace BTCPayServer.Controllers
|
namespace BTCPayServer.Controllers
|
||||||
{
|
{
|
||||||
@@ -31,16 +32,19 @@ namespace BTCPayServer.Controllers
|
|||||||
Network _Network;
|
Network _Network;
|
||||||
InvoiceWatcher _Watcher;
|
InvoiceWatcher _Watcher;
|
||||||
ExplorerClient _Explorer;
|
ExplorerClient _Explorer;
|
||||||
|
BTCPayServerOptions _Options;
|
||||||
|
|
||||||
public CallbackController(SettingsRepository repo,
|
public CallbackController(SettingsRepository repo,
|
||||||
ExplorerClient explorer,
|
ExplorerClient explorer,
|
||||||
InvoiceWatcher watcher,
|
InvoiceWatcherAccessor watcher,
|
||||||
|
BTCPayServerOptions options,
|
||||||
Network network)
|
Network network)
|
||||||
{
|
{
|
||||||
_Settings = repo;
|
_Settings = repo;
|
||||||
_Network = network;
|
_Network = network;
|
||||||
_Watcher = watcher;
|
_Watcher = watcher.Instance;
|
||||||
_Explorer = explorer;
|
_Explorer = explorer;
|
||||||
|
_Options = options;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Route("callbacks/transactions")]
|
[Route("callbacks/transactions")]
|
||||||
@@ -79,7 +83,7 @@ namespace BTCPayServer.Controllers
|
|||||||
public async Task<Uri> GetCallbackUriAsync(HttpRequest request)
|
public async Task<Uri> GetCallbackUriAsync(HttpRequest request)
|
||||||
{
|
{
|
||||||
string token = await GetToken();
|
string token = await GetToken();
|
||||||
return new Uri(request.GetAbsoluteRoot() + "/callbacks/transactions?token=" + token);
|
return BuildCallbackUri(request, "callbacks/transactions?token=" + token);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task RegisterCallbackUriAsync(DerivationStrategyBase derivationScheme, HttpRequest request)
|
public async Task RegisterCallbackUriAsync(DerivationStrategyBase derivationScheme, HttpRequest request)
|
||||||
@@ -103,12 +107,18 @@ namespace BTCPayServer.Controllers
|
|||||||
public async Task<Uri> GetCallbackBlockUriAsync(HttpRequest request)
|
public async Task<Uri> GetCallbackBlockUriAsync(HttpRequest request)
|
||||||
{
|
{
|
||||||
string token = await GetToken();
|
string token = await GetToken();
|
||||||
return new Uri(request.GetAbsoluteRoot() + "/callbacks/blocks?token=" + token);
|
return BuildCallbackUri(request, "callbacks/blocks?token=" + token);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Uri> RegisterCallbackBlockUriAsync(HttpRequest request)
|
private Uri BuildCallbackUri(HttpRequest request, string callbackPath)
|
||||||
|
{
|
||||||
|
string baseUrl = _Options.InternalUrl == null ? request.GetAbsoluteRoot() : _Options.InternalUrl.AbsolutePath;
|
||||||
|
baseUrl = baseUrl.WithTrailingSlash();
|
||||||
|
return new Uri(baseUrl + callbackPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Uri> RegisterCallbackBlockUriAsync(Uri uri)
|
||||||
{
|
{
|
||||||
var uri = await GetCallbackBlockUriAsync(request);
|
|
||||||
await _Explorer.SubscribeToBlocksAsync(uri);
|
await _Explorer.SubscribeToBlocksAsync(uri);
|
||||||
return uri;
|
return uri;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ namespace BTCPayServer.Controllers
|
|||||||
BTCPayWallet wallet,
|
BTCPayWallet wallet,
|
||||||
IRateProvider rateProvider,
|
IRateProvider rateProvider,
|
||||||
StoreRepository storeRepository,
|
StoreRepository storeRepository,
|
||||||
InvoiceWatcher watcher,
|
InvoiceWatcherAccessor watcher,
|
||||||
ExplorerClient explorerClient,
|
ExplorerClient explorerClient,
|
||||||
IFeeProvider feeProvider)
|
IFeeProvider feeProvider)
|
||||||
{
|
{
|
||||||
@@ -73,7 +73,7 @@ namespace BTCPayServer.Controllers
|
|||||||
_InvoiceRepository = invoiceRepository ?? throw new ArgumentNullException(nameof(invoiceRepository));
|
_InvoiceRepository = invoiceRepository ?? throw new ArgumentNullException(nameof(invoiceRepository));
|
||||||
_Wallet = wallet ?? throw new ArgumentNullException(nameof(wallet));
|
_Wallet = wallet ?? throw new ArgumentNullException(nameof(wallet));
|
||||||
_RateProvider = rateProvider ?? throw new ArgumentNullException(nameof(rateProvider));
|
_RateProvider = rateProvider ?? throw new ArgumentNullException(nameof(rateProvider));
|
||||||
_Watcher = watcher ?? throw new ArgumentNullException(nameof(watcher));
|
_Watcher = (watcher ?? throw new ArgumentNullException(nameof(watcher))).Instance;
|
||||||
_UserManager = userManager;
|
_UserManager = userManager;
|
||||||
_FeeProvider = feeProvider ?? throw new ArgumentNullException(nameof(feeProvider));
|
_FeeProvider = feeProvider ?? throw new ArgumentNullException(nameof(feeProvider));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,10 +71,10 @@ namespace BTCPayServer
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HtmlString ToSrvModel(this object o)
|
public static HtmlString ToJSVariableModel(this object o, string variableName)
|
||||||
{
|
{
|
||||||
var encodedJson = JavaScriptEncoder.Default.Encode(o.ToJson());
|
var encodedJson = JavaScriptEncoder.Default.Encode(o.ToJson());
|
||||||
return new HtmlString("var srvModel = JSON.parse('" + encodedJson + "');");
|
return new HtmlString($"var {variableName} = JSON.parse('" + encodedJson + "');");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using BTCPayServer.Configuration;
|
using BTCPayServer.Configuration;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -34,6 +35,7 @@ using System.Threading;
|
|||||||
using BTCPayServer.Services.Wallets;
|
using BTCPayServer.Services.Wallets;
|
||||||
using BTCPayServer.Authentication;
|
using BTCPayServer.Authentication;
|
||||||
using Microsoft.Extensions.Caching.Memory;
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
|
using BTCPayServer.Logging;
|
||||||
|
|
||||||
namespace BTCPayServer.Hosting
|
namespace BTCPayServer.Hosting
|
||||||
{
|
{
|
||||||
@@ -83,19 +85,6 @@ namespace BTCPayServer.Hosting
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
class BTCPayServerConfigureOptions : IConfigureOptions<MvcOptions>
|
|
||||||
{
|
|
||||||
BTCPayServerOptions _Options;
|
|
||||||
public BTCPayServerConfigureOptions(BTCPayServerOptions options)
|
|
||||||
{
|
|
||||||
_Options = options;
|
|
||||||
}
|
|
||||||
public void Configure(MvcOptions options)
|
|
||||||
{
|
|
||||||
if (_Options.RequireHttps)
|
|
||||||
options.Filters.Add(new RequireHttpsAttribute());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public static IServiceCollection AddBTCPayServer(this IServiceCollection services)
|
public static IServiceCollection AddBTCPayServer(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddDbContext<ApplicationDbContext>((provider, o) =>
|
services.AddDbContext<ApplicationDbContext>((provider, o) =>
|
||||||
@@ -106,18 +95,35 @@ namespace BTCPayServer.Hosting
|
|||||||
services.TryAddSingleton<SettingsRepository>();
|
services.TryAddSingleton<SettingsRepository>();
|
||||||
services.TryAddSingleton<InvoicePaymentNotification>();
|
services.TryAddSingleton<InvoicePaymentNotification>();
|
||||||
services.TryAddSingleton<BTCPayServerOptions>(o => o.GetRequiredService<IOptions<BTCPayServerOptions>>().Value);
|
services.TryAddSingleton<BTCPayServerOptions>(o => o.GetRequiredService<IOptions<BTCPayServerOptions>>().Value);
|
||||||
services.TryAddSingleton<IConfigureOptions<MvcOptions>, BTCPayServerConfigureOptions>();
|
services.TryAddSingleton<InvoiceRepository>(o =>
|
||||||
services.TryAddSingleton(o =>
|
|
||||||
{
|
{
|
||||||
var runtime = new BTCPayServerRuntime();
|
var opts = o.GetRequiredService<BTCPayServerOptions>();
|
||||||
runtime.Configure(o.GetRequiredService<BTCPayServerOptions>());
|
var dbContext = o.GetRequiredService<ApplicationDbContextFactory>();
|
||||||
return runtime;
|
var dbpath = Path.Combine(opts.DataDir, "InvoiceDB");
|
||||||
|
if (!Directory.Exists(dbpath))
|
||||||
|
Directory.CreateDirectory(dbpath);
|
||||||
|
return new InvoiceRepository(dbContext, dbpath, opts.Network);
|
||||||
});
|
});
|
||||||
services.AddSingleton<BTCPayServerEnvironment>();
|
services.AddSingleton<BTCPayServerEnvironment>();
|
||||||
services.TryAddSingleton<TokenRepository>();
|
services.TryAddSingleton<TokenRepository>();
|
||||||
services.TryAddSingleton(o => o.GetRequiredService<BTCPayServerRuntime>().InvoiceRepository);
|
|
||||||
services.TryAddSingleton<Network>(o => o.GetRequiredService<BTCPayServerOptions>().Network);
|
services.TryAddSingleton<Network>(o => o.GetRequiredService<BTCPayServerOptions>().Network);
|
||||||
services.TryAddSingleton<ApplicationDbContextFactory>(o => o.GetRequiredService<BTCPayServerRuntime>().DBFactory);
|
services.TryAddSingleton<ApplicationDbContextFactory>(o =>
|
||||||
|
{
|
||||||
|
var opts = o.GetRequiredService<BTCPayServerOptions>();
|
||||||
|
ApplicationDbContextFactory dbContext = null;
|
||||||
|
if (opts.PostgresConnectionString == null)
|
||||||
|
{
|
||||||
|
var connStr = "Data Source=" + Path.Combine(opts.DataDir, "sqllite.db");
|
||||||
|
Logs.Configuration.LogInformation($"SQLite DB used ({connStr})");
|
||||||
|
dbContext = new ApplicationDbContextFactory(DatabaseType.Sqlite, connStr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logs.Configuration.LogInformation($"Postgres DB used ({opts.PostgresConnectionString})");
|
||||||
|
dbContext = new ApplicationDbContextFactory(DatabaseType.Postgres, opts.PostgresConnectionString);
|
||||||
|
}
|
||||||
|
return dbContext;
|
||||||
|
});
|
||||||
services.TryAddSingleton<StoreRepository>();
|
services.TryAddSingleton<StoreRepository>();
|
||||||
services.TryAddSingleton<BTCPayWallet>();
|
services.TryAddSingleton<BTCPayWallet>();
|
||||||
services.TryAddSingleton<CurrencyNameTable>();
|
services.TryAddSingleton<CurrencyNameTable>();
|
||||||
@@ -127,10 +133,16 @@ namespace BTCPayServer.Hosting
|
|||||||
BlockTarget = 20,
|
BlockTarget = 20,
|
||||||
ExplorerClient = o.GetRequiredService<ExplorerClient>()
|
ExplorerClient = o.GetRequiredService<ExplorerClient>()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
services.TryAddSingleton<NBXplorerWaiterAccessor>();
|
||||||
|
services.AddSingleton<IHostedService, NBXplorerWaiter>();
|
||||||
services.TryAddSingleton<ExplorerClient>(o =>
|
services.TryAddSingleton<ExplorerClient>(o =>
|
||||||
{
|
{
|
||||||
var runtime = o.GetRequiredService<BTCPayServerRuntime>();
|
var opts = o.GetRequiredService<BTCPayServerOptions>();
|
||||||
return runtime.Explorer;
|
var explorer = new ExplorerClient(opts.Network, opts.Explorer);
|
||||||
|
if (!explorer.SetCookieAuth(opts.CookieFile))
|
||||||
|
explorer.SetNoAuth();
|
||||||
|
return explorer;
|
||||||
});
|
});
|
||||||
services.TryAddSingleton<Bitpay>(o =>
|
services.TryAddSingleton<Bitpay>(o =>
|
||||||
{
|
{
|
||||||
@@ -145,9 +157,12 @@ namespace BTCPayServer.Hosting
|
|||||||
var bitpay = new BitpayRateProvider(new Bitpay(new Key(), new Uri("https://bitpay.com/")));
|
var bitpay = new BitpayRateProvider(new Bitpay(new Key(), new Uri("https://bitpay.com/")));
|
||||||
return new CachedRateProvider(new FallbackRateProvider(new IRateProvider[] { coinaverage, bitpay }), o.GetRequiredService<IMemoryCache>()) { CacheSpan = TimeSpan.FromMinutes(1.0) };
|
return new CachedRateProvider(new FallbackRateProvider(new IRateProvider[] { coinaverage, bitpay }), o.GetRequiredService<IMemoryCache>()) { CacheSpan = TimeSpan.FromMinutes(1.0) };
|
||||||
});
|
});
|
||||||
services.TryAddSingleton<InvoiceWatcher>();
|
|
||||||
services.TryAddSingleton<InvoiceNotificationManager>();
|
services.TryAddSingleton<InvoiceNotificationManager>();
|
||||||
services.TryAddSingleton<IHostedService>(o => o.GetRequiredService<InvoiceWatcher>());
|
|
||||||
|
services.TryAddSingleton<InvoiceWatcherAccessor>();
|
||||||
|
services.AddSingleton<IHostedService, InvoiceWatcher>();
|
||||||
|
|
||||||
services.TryAddScoped<IHttpContextAccessor, HttpContextAccessor>();
|
services.TryAddScoped<IHttpContextAccessor, HttpContextAccessor>();
|
||||||
services.TryAddSingleton<IAuthorizationHandler, OwnStoreHandler>();
|
services.TryAddSingleton<IAuthorizationHandler, OwnStoreHandler>();
|
||||||
services.AddTransient<AccessTokenController>();
|
services.AddTransient<AccessTokenController>();
|
||||||
@@ -174,12 +189,6 @@ namespace BTCPayServer.Hosting
|
|||||||
|
|
||||||
public static IApplicationBuilder UsePayServer(this IApplicationBuilder app)
|
public static IApplicationBuilder UsePayServer(this IApplicationBuilder app)
|
||||||
{
|
{
|
||||||
if (app.ApplicationServices.GetRequiredService<BTCPayServerOptions>().RequireHttps)
|
|
||||||
{
|
|
||||||
var options = new RewriteOptions().AddRedirectToHttps();
|
|
||||||
app.UseRewriter(options);
|
|
||||||
}
|
|
||||||
|
|
||||||
using (var scope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
|
using (var scope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
|
||||||
{
|
{
|
||||||
//Wait the DB is ready
|
//Wait the DB is ready
|
||||||
|
|||||||
@@ -31,71 +31,28 @@ namespace BTCPayServer.Hosting
|
|||||||
RequestDelegate _Next;
|
RequestDelegate _Next;
|
||||||
CallbackController _CallbackController;
|
CallbackController _CallbackController;
|
||||||
BTCPayServerOptions _Options;
|
BTCPayServerOptions _Options;
|
||||||
|
private NBXplorerWaiterAccessor _NbxplorerAwaiter;
|
||||||
|
|
||||||
public BTCPayMiddleware(RequestDelegate next,
|
public BTCPayMiddleware(RequestDelegate next,
|
||||||
TokenRepository tokenRepo,
|
TokenRepository tokenRepo,
|
||||||
BTCPayServerOptions options,
|
BTCPayServerOptions options,
|
||||||
|
NBXplorerWaiterAccessor nbxplorerAwaiter,
|
||||||
CallbackController callbackController)
|
CallbackController callbackController)
|
||||||
{
|
{
|
||||||
_TokenRepository = tokenRepo ?? throw new ArgumentNullException(nameof(tokenRepo));
|
_TokenRepository = tokenRepo ?? throw new ArgumentNullException(nameof(tokenRepo));
|
||||||
_Next = next ?? throw new ArgumentNullException(nameof(next));
|
_Next = next ?? throw new ArgumentNullException(nameof(next));
|
||||||
_CallbackController = callbackController;
|
_CallbackController = callbackController;
|
||||||
_Options = options ?? throw new ArgumentNullException(nameof(options));
|
_Options = options ?? throw new ArgumentNullException(nameof(options));
|
||||||
|
_NbxplorerAwaiter = (nbxplorerAwaiter ?? throw new ArgumentNullException(nameof(nbxplorerAwaiter)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool _Registered;
|
bool _Registered;
|
||||||
public async Task Invoke(HttpContext httpContext)
|
public async Task Invoke(HttpContext httpContext)
|
||||||
{
|
{
|
||||||
if (!_Registered)
|
RewriteHostIfNeeded(httpContext);
|
||||||
{
|
await EnsureBlockCallbackRegistered(httpContext);
|
||||||
var callback = await _CallbackController.RegisterCallbackBlockUriAsync(httpContext.Request);
|
|
||||||
Logs.PayServer.LogInformation($"Registering block callback to " + callback);
|
|
||||||
_Registered = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that code executing after this point think that the external url has been hit.
|
|
||||||
if(_Options.ExternalUrl != null)
|
|
||||||
{
|
|
||||||
httpContext.Request.Scheme = _Options.ExternalUrl.Scheme;
|
|
||||||
if(_Options.ExternalUrl.IsDefaultPort)
|
|
||||||
httpContext.Request.Host = new HostString(_Options.ExternalUrl.Host);
|
|
||||||
else
|
|
||||||
httpContext.Request.Host = new HostString(_Options.ExternalUrl.Host, _Options.ExternalUrl.Port);
|
|
||||||
}
|
|
||||||
// NGINX pass X-Forwarded-Proto and X-Forwarded-Port, so let's use that to have better guess of the real domain
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ushort? p = null;
|
|
||||||
if(httpContext.Request.Headers.TryGetValue("X-Forwarded-Proto", out StringValues proto))
|
|
||||||
{
|
|
||||||
var scheme = proto.SingleOrDefault();
|
|
||||||
if(scheme != null)
|
|
||||||
{
|
|
||||||
httpContext.Request.Scheme = scheme;
|
|
||||||
if (scheme == "http")
|
|
||||||
p = 80;
|
|
||||||
if (scheme == "https")
|
|
||||||
p = 443;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (httpContext.Request.Headers.TryGetValue("X-Forwarded-Port", out StringValues port))
|
|
||||||
{
|
|
||||||
var portString = port.SingleOrDefault();
|
|
||||||
if(portString != null && ushort.TryParse(portString, out ushort pp))
|
|
||||||
{
|
|
||||||
p = pp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(p.HasValue)
|
|
||||||
{
|
|
||||||
bool isDefault = httpContext.Request.Scheme == "http" && p.Value == 80;
|
|
||||||
isDefault |= httpContext.Request.Scheme == "https" && p.Value == 443;
|
|
||||||
if (isDefault)
|
|
||||||
httpContext.Request.Host = new HostString(httpContext.Request.Host.Host);
|
|
||||||
else
|
|
||||||
httpContext.Request.Host = new HostString(httpContext.Request.Host.Host, p.Value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
httpContext.Request.Headers.TryGetValue("x-signature", out StringValues values);
|
httpContext.Request.Headers.TryGetValue("x-signature", out StringValues values);
|
||||||
var sig = values.FirstOrDefault();
|
var sig = values.FirstOrDefault();
|
||||||
@@ -150,6 +107,75 @@ namespace BTCPayServer.Hosting
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void RewriteHostIfNeeded(HttpContext httpContext)
|
||||||
|
{
|
||||||
|
// Make sure that code executing after this point think that the external url has been hit.
|
||||||
|
if (_Options.ExternalUrl != null)
|
||||||
|
{
|
||||||
|
httpContext.Request.Scheme = _Options.ExternalUrl.Scheme;
|
||||||
|
if (_Options.ExternalUrl.IsDefaultPort)
|
||||||
|
httpContext.Request.Host = new HostString(_Options.ExternalUrl.Host);
|
||||||
|
else
|
||||||
|
httpContext.Request.Host = new HostString(_Options.ExternalUrl.Host, _Options.ExternalUrl.Port);
|
||||||
|
}
|
||||||
|
// NGINX pass X-Forwarded-Proto and X-Forwarded-Port, so let's use that to have better guess of the real domain
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ushort? p = null;
|
||||||
|
if (httpContext.Request.Headers.TryGetValue("X-Forwarded-Proto", out StringValues proto))
|
||||||
|
{
|
||||||
|
var scheme = proto.SingleOrDefault();
|
||||||
|
if (scheme != null)
|
||||||
|
{
|
||||||
|
httpContext.Request.Scheme = scheme;
|
||||||
|
if (scheme == "http")
|
||||||
|
p = 80;
|
||||||
|
if (scheme == "https")
|
||||||
|
p = 443;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (httpContext.Request.Headers.TryGetValue("X-Forwarded-Port", out StringValues port))
|
||||||
|
{
|
||||||
|
var portString = port.SingleOrDefault();
|
||||||
|
if (portString != null && ushort.TryParse(portString, out ushort pp))
|
||||||
|
{
|
||||||
|
p = pp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (p.HasValue)
|
||||||
|
{
|
||||||
|
bool isDefault = httpContext.Request.Scheme == "http" && p.Value == 80;
|
||||||
|
isDefault |= httpContext.Request.Scheme == "https" && p.Value == 443;
|
||||||
|
if (isDefault)
|
||||||
|
httpContext.Request.Host = new HostString(httpContext.Request.Host.Host);
|
||||||
|
else
|
||||||
|
httpContext.Request.Host = new HostString(httpContext.Request.Host.Host, p.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task EnsureBlockCallbackRegistered(HttpContext httpContext)
|
||||||
|
{
|
||||||
|
if (!_Registered)
|
||||||
|
{
|
||||||
|
var callback = await _CallbackController.GetCallbackBlockUriAsync(httpContext.Request);
|
||||||
|
var unused = _NbxplorerAwaiter.Instance.WhenReady(async c =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _CallbackController.RegisterCallbackBlockUriAsync(callback);
|
||||||
|
Logs.PayServer.LogInformation($"Registering block callback to " + callback);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logs.PayServer.LogError(ex, "Could not register block callback");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
_Registered = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static async Task HandleBitpayHttpException(HttpContext httpContext, BitpayHttpException ex)
|
private static async Task HandleBitpayHttpException(HttpContext httpContext, BitpayHttpException ex)
|
||||||
{
|
{
|
||||||
httpContext.Response.StatusCode = ex.StatusCode;
|
httpContext.Response.StatusCode = ex.StatusCode;
|
||||||
|
|||||||
184
BTCPayServer/NBXplorerWaiter.cs
Normal file
184
BTCPayServer/NBXplorerWaiter.cs
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using BTCPayServer.Logging;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using NBXplorer;
|
||||||
|
using NBXplorer.Models;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
|
namespace BTCPayServer
|
||||||
|
{
|
||||||
|
public class NBXplorerWaiterAccessor
|
||||||
|
{
|
||||||
|
public NBXplorerWaiter Instance { get; set; }
|
||||||
|
}
|
||||||
|
public enum NBXplorerState
|
||||||
|
{
|
||||||
|
NotConnected,
|
||||||
|
Synching,
|
||||||
|
Ready
|
||||||
|
}
|
||||||
|
public class NBXplorerWaiter : IHostedService
|
||||||
|
{
|
||||||
|
public NBXplorerWaiter(ExplorerClient client, NBXplorerWaiterAccessor accessor)
|
||||||
|
{
|
||||||
|
_Client = client;
|
||||||
|
accessor.Instance = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExplorerClient _Client;
|
||||||
|
Timer _Timer;
|
||||||
|
ManualResetEventSlim _Idle = new ManualResetEventSlim(true);
|
||||||
|
public Task StartAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
_Timer = new Timer(Callback, null, 0, (int)TimeSpan.FromMinutes(1.0).TotalMilliseconds);
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Callback(object state)
|
||||||
|
{
|
||||||
|
if (!_Idle.IsSet)
|
||||||
|
return;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_Idle.Reset();
|
||||||
|
CheckStatus().GetAwaiter().GetResult();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logs.PayServer.LogError(ex, "Error while checking NBXplorer state");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_Idle.Set();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task CheckStatus()
|
||||||
|
{
|
||||||
|
while (await StepAsync())
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
List<Task> tasks = new List<Task>();
|
||||||
|
if (State == NBXplorerState.Ready)
|
||||||
|
{
|
||||||
|
while (_WhenReady.TryDequeue(out Func<ExplorerClient, Task> act))
|
||||||
|
{
|
||||||
|
tasks.Add(act(_Client));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await Task.WhenAll(tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<bool> StepAsync()
|
||||||
|
{
|
||||||
|
var oldState = State;
|
||||||
|
|
||||||
|
StatusResult status = null;
|
||||||
|
switch (State)
|
||||||
|
{
|
||||||
|
case NBXplorerState.NotConnected:
|
||||||
|
status = await GetStatusWithTimeout();
|
||||||
|
if (status != null)
|
||||||
|
{
|
||||||
|
if (status.IsFullySynched())
|
||||||
|
{
|
||||||
|
State = NBXplorerState.Ready;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
State = NBXplorerState.Synching;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case NBXplorerState.Synching:
|
||||||
|
status = await GetStatusWithTimeout();
|
||||||
|
if (status == null)
|
||||||
|
{
|
||||||
|
State = NBXplorerState.NotConnected;
|
||||||
|
}
|
||||||
|
else if (status.IsFullySynched())
|
||||||
|
{
|
||||||
|
State = NBXplorerState.Ready;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case NBXplorerState.Ready:
|
||||||
|
status = await GetStatusWithTimeout();
|
||||||
|
if (status == null)
|
||||||
|
{
|
||||||
|
State = NBXplorerState.NotConnected;
|
||||||
|
}
|
||||||
|
else if (!status.IsFullySynched())
|
||||||
|
{
|
||||||
|
State = NBXplorerState.Synching;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
LastStatus = status;
|
||||||
|
if (oldState != State)
|
||||||
|
{
|
||||||
|
Logs.PayServer.LogInformation($"NBXplorerWaiter status changed: {oldState} => {State}");
|
||||||
|
}
|
||||||
|
return oldState != State;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<T> WhenReady<T>(Func<ExplorerClient, Task<T>> act)
|
||||||
|
{
|
||||||
|
if (State == NBXplorerState.Ready)
|
||||||
|
return act(_Client);
|
||||||
|
TaskCompletionSource<T> completion = new TaskCompletionSource<T>();
|
||||||
|
_WhenReady.Enqueue(async client =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await act(client);
|
||||||
|
completion.SetResult(result);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
completion.SetException(ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return completion.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConcurrentQueue<Func<ExplorerClient, Task>> _WhenReady = new ConcurrentQueue<Func<ExplorerClient, Task>>();
|
||||||
|
|
||||||
|
private async Task<StatusResult> GetStatusWithTimeout()
|
||||||
|
{
|
||||||
|
CancellationTokenSource cts = new CancellationTokenSource();
|
||||||
|
using (cts)
|
||||||
|
{
|
||||||
|
var cancellation = cts.Token;
|
||||||
|
while (!cancellation.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var status = await _Client.GetStatusAsync(cancellation).ConfigureAwait(false);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException) { throw; }
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NBXplorerState State { get; private set; }
|
||||||
|
|
||||||
|
public StatusResult LastStatus { get; private set; }
|
||||||
|
|
||||||
|
public Task StopAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
_Timer.Dispose();
|
||||||
|
_Timer = null;
|
||||||
|
_Idle.Wait();
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,6 +16,10 @@ using BTCPayServer.Services.Wallets;
|
|||||||
|
|
||||||
namespace BTCPayServer.Services.Invoices
|
namespace BTCPayServer.Services.Invoices
|
||||||
{
|
{
|
||||||
|
public class InvoiceWatcherAccessor
|
||||||
|
{
|
||||||
|
public InvoiceWatcher Instance { get; set; }
|
||||||
|
}
|
||||||
public class InvoiceWatcher : IHostedService
|
public class InvoiceWatcher : IHostedService
|
||||||
{
|
{
|
||||||
InvoiceRepository _InvoiceRepository;
|
InvoiceRepository _InvoiceRepository;
|
||||||
@@ -23,11 +27,13 @@ namespace BTCPayServer.Services.Invoices
|
|||||||
DerivationStrategyFactory _DerivationFactory;
|
DerivationStrategyFactory _DerivationFactory;
|
||||||
InvoiceNotificationManager _NotificationManager;
|
InvoiceNotificationManager _NotificationManager;
|
||||||
BTCPayWallet _Wallet;
|
BTCPayWallet _Wallet;
|
||||||
|
|
||||||
|
|
||||||
public InvoiceWatcher(ExplorerClient explorerClient,
|
public InvoiceWatcher(ExplorerClient explorerClient,
|
||||||
InvoiceRepository invoiceRepository,
|
InvoiceRepository invoiceRepository,
|
||||||
BTCPayWallet wallet,
|
BTCPayWallet wallet,
|
||||||
InvoiceNotificationManager notificationManager)
|
InvoiceNotificationManager notificationManager,
|
||||||
|
InvoiceWatcherAccessor accessor)
|
||||||
{
|
{
|
||||||
LongPollingMode = explorerClient.Network == Network.RegTest;
|
LongPollingMode = explorerClient.Network == Network.RegTest;
|
||||||
PollInterval = explorerClient.Network == Network.RegTest ? TimeSpan.FromSeconds(10.0) : TimeSpan.FromMinutes(1.0);
|
PollInterval = explorerClient.Network == Network.RegTest ? TimeSpan.FromSeconds(10.0) : TimeSpan.FromMinutes(1.0);
|
||||||
@@ -36,6 +42,7 @@ namespace BTCPayServer.Services.Invoices
|
|||||||
_DerivationFactory = new DerivationStrategyFactory(_ExplorerClient.Network);
|
_DerivationFactory = new DerivationStrategyFactory(_ExplorerClient.Network);
|
||||||
_InvoiceRepository = invoiceRepository ?? throw new ArgumentNullException(nameof(invoiceRepository));
|
_InvoiceRepository = invoiceRepository ?? throw new ArgumentNullException(nameof(invoiceRepository));
|
||||||
_NotificationManager = notificationManager ?? throw new ArgumentNullException(nameof(notificationManager));
|
_NotificationManager = notificationManager ?? throw new ArgumentNullException(nameof(notificationManager));
|
||||||
|
accessor.Instance = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool LongPollingMode
|
public bool LongPollingMode
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
<script src="https://code.jquery.com/jquery-3.2.1.min.js"
|
<script src="https://code.jquery.com/jquery-3.2.1.min.js"
|
||||||
crossorigin="anonymous"></script>
|
crossorigin="anonymous"></script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
@Model.ToSrvModel()
|
@Model.ToJSVariableModel("srvModel")
|
||||||
</script>
|
</script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/1.7.1/clipboard.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/1.7.1/clipboard.min.js"></script>
|
||||||
<script src="~/js/vue.js" type="text/javascript" defer="defer"></script>
|
<script src="~/js/vue.js" type="text/javascript" defer="defer"></script>
|
||||||
|
|||||||
@@ -78,6 +78,33 @@
|
|||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
@RenderBody()
|
@RenderBody()
|
||||||
|
|
||||||
|
<!-- Modal -->
|
||||||
|
<div id="myModal" class="modal fade" role="dialog">
|
||||||
|
<form method="post" action="/invoices/invalidatepaid">
|
||||||
|
<input id="invoiceId" name="invoiceId" type="hidden" />
|
||||||
|
<div class="modal-dialog">
|
||||||
|
|
||||||
|
<!-- Modal content-->
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h4 class="modal-title">Set Invoice status to Invalid</h4>
|
||||||
|
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p>Are you sure you want to invalidate this transaction? This action is NOT undoable!</p>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="submit" class="btn btn-danger">Yes, make invoice Invalid</button>
|
||||||
|
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<footer class="bg-dark">
|
<footer class="bg-dark">
|
||||||
<div class="container text-right"><span style="font-size:8px;">@env.ToString()</span></div>
|
<div class="container text-right"><span style="font-size:8px;">@env.ToString()</span></div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ RUN dotnet restore
|
|||||||
COPY BTCPayServer/. .
|
COPY BTCPayServer/. .
|
||||||
RUN dotnet publish --output /app/ --configuration Release
|
RUN dotnet publish --output /app/ --configuration Release
|
||||||
|
|
||||||
FROM microsoft/aspnetcore:2.0.0
|
FROM microsoft/aspnetcore:2.0.3
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
RUN mkdir /datadir
|
RUN mkdir /datadir
|
||||||
@@ -14,4 +14,4 @@ ENV BTCPAY_DATADIR=/datadir
|
|||||||
VOLUME /datadir
|
VOLUME /datadir
|
||||||
|
|
||||||
COPY --from=builder "/app" .
|
COPY --from=builder "/app" .
|
||||||
ENTRYPOINT ["dotnet", "BTCPayServer.dll"]
|
ENTRYPOINT ["dotnet", "BTCPayServer.dll"]
|
||||||
|
|||||||
Reference in New Issue
Block a user