mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-18 06:24:24 +01:00
Can start without NBXplorer being ready
This commit is contained in:
@@ -7,9 +7,9 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<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.runner.visualstudio" Version="2.2.0" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -106,19 +106,21 @@ namespace BTCPayServer.Tests
|
||||
.UseStartup<Startup>()
|
||||
.Build();
|
||||
_Host.Start();
|
||||
Runtime = (BTCPayServerRuntime)_Host.Services.GetService(typeof(BTCPayServerRuntime));
|
||||
var watcher = (InvoiceWatcher)_Host.Services.GetService(typeof(InvoiceWatcher));
|
||||
}
|
||||
InvoiceRepository = (InvoiceRepository)_Host.Services.GetService(typeof(InvoiceRepository));
|
||||
|
||||
public BTCPayServerRuntime Runtime
|
||||
{
|
||||
get; set;
|
||||
var waiter = ((NBXplorerWaiterAccessor)_Host.Services.GetService(typeof(NBXplorerWaiterAccessor))).Instance;
|
||||
while(waiter.State != NBXplorerState.Ready)
|
||||
{
|
||||
Thread.Sleep(10);
|
||||
}
|
||||
}
|
||||
|
||||
public string HostName
|
||||
{
|
||||
get;
|
||||
internal set;
|
||||
}
|
||||
public InvoiceRepository InvoiceRepository { get; private set; }
|
||||
|
||||
public T GetService<T>()
|
||||
{
|
||||
|
||||
@@ -307,13 +307,13 @@ namespace BTCPayServer.Tests
|
||||
|
||||
Eventually(() =>
|
||||
{
|
||||
var textSearchResult = tester.PayTester.Runtime.InvoiceRepository.GetInvoices(new InvoiceQuery()
|
||||
var textSearchResult = tester.PayTester.InvoiceRepository.GetInvoices(new InvoiceQuery()
|
||||
{
|
||||
StoreId = user.StoreId,
|
||||
TextSearch = invoice.OrderId
|
||||
}).GetAwaiter().GetResult();
|
||||
Assert.Equal(1, textSearchResult.Length);
|
||||
textSearchResult = tester.PayTester.Runtime.InvoiceRepository.GetInvoices(new InvoiceQuery()
|
||||
textSearchResult = tester.PayTester.InvoiceRepository.GetInvoices(new InvoiceQuery()
|
||||
{
|
||||
StoreId = user.StoreId,
|
||||
TextSearch = invoice.Id
|
||||
|
||||
@@ -13,12 +13,15 @@ services:
|
||||
TESTS_RPCCONNECTION: server=http://bitcoind:43782;ceiwHEbqWI83:DwubwWsoo3
|
||||
TESTS_NBXPLORERURL: http://nbxplorer:32838/
|
||||
TESTS_POSTGRES: User ID=postgres;Host=postgres;Port=5432;Database=btcpayserver
|
||||
TESTS_FAKECALLBACK: 'true'
|
||||
TESTS_FAKECALLBACK: 'false'
|
||||
TESTS_PORT: 80
|
||||
TESTS_HOSTNAME: tests
|
||||
TEST_ECLAIR1: http://eclair1:8080/
|
||||
TEST_ECLAIR2: http://eclair2:8080/
|
||||
expose:
|
||||
- "80"
|
||||
links:
|
||||
- bitcoind
|
||||
- nbxplorer
|
||||
- eclair1
|
||||
- eclair2
|
||||
@@ -39,7 +42,7 @@ services:
|
||||
- eclair2
|
||||
|
||||
nbxplorer:
|
||||
image: nicolasdorier/nbxplorer:1.0.0.29
|
||||
image: nicolasdorier/nbxplorer:1.0.0.32
|
||||
ports:
|
||||
- "32838:32838"
|
||||
expose:
|
||||
|
||||
@@ -18,13 +18,13 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<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="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="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.Configuration" Version="1.0.0.2" />
|
||||
<PackageReference Include="NicolasDorier.StandardConfiguration" Version="1.0.0.13" />
|
||||
@@ -34,9 +34,9 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.0.0" PrivateAssets="All" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.0.0" PrivateAssets="All" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.0.1" PrivateAssets="All" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.0.1" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -55,14 +55,9 @@ namespace BTCPayServer.Configuration
|
||||
|
||||
Explorer = conf.GetOrDefault<Uri>("explorer.url", networkInfo.DefaultExplorerUrl);
|
||||
CookieFile = conf.GetOrDefault<string>("explorer.cookiefile", networkInfo.DefaultExplorerCookieFile);
|
||||
RequireHttps = conf.GetOrDefault<bool>("requirehttps", false);
|
||||
PostgresConnectionString = conf.GetOrDefault<string>("postgres", null);
|
||||
ExternalUrl = conf.GetOrDefault<Uri>("externalurl", null);
|
||||
}
|
||||
|
||||
public bool RequireHttps
|
||||
{
|
||||
get; set;
|
||||
InternalUrl = conf.GetOrDefault<Uri>("internalurl", null);
|
||||
}
|
||||
public string PostgresConnectionString
|
||||
{
|
||||
@@ -74,5 +69,6 @@ namespace BTCPayServer.Configuration
|
||||
get;
|
||||
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("--testnet | -testnet", $"Use testnet", 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("--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("--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;
|
||||
}
|
||||
|
||||
@@ -77,7 +77,6 @@ namespace BTCPayServer.Configuration
|
||||
builder.AppendLine("#regtest=0");
|
||||
builder.AppendLine();
|
||||
builder.AppendLine("### Server settings ###");
|
||||
builder.AppendLine("#requirehttps=0");
|
||||
builder.AppendLine("#port=" + network.DefaultPort);
|
||||
builder.AppendLine("#bind=127.0.0.1");
|
||||
builder.AppendLine();
|
||||
|
||||
@@ -15,6 +15,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Configuration;
|
||||
|
||||
namespace BTCPayServer.Controllers
|
||||
{
|
||||
@@ -31,16 +32,19 @@ namespace BTCPayServer.Controllers
|
||||
Network _Network;
|
||||
InvoiceWatcher _Watcher;
|
||||
ExplorerClient _Explorer;
|
||||
BTCPayServerOptions _Options;
|
||||
|
||||
public CallbackController(SettingsRepository repo,
|
||||
ExplorerClient explorer,
|
||||
InvoiceWatcher watcher,
|
||||
InvoiceWatcherAccessor watcher,
|
||||
BTCPayServerOptions options,
|
||||
Network network)
|
||||
{
|
||||
_Settings = repo;
|
||||
_Network = network;
|
||||
_Watcher = watcher;
|
||||
_Watcher = watcher.Instance;
|
||||
_Explorer = explorer;
|
||||
_Options = options;
|
||||
}
|
||||
|
||||
[Route("callbacks/transactions")]
|
||||
@@ -79,7 +83,7 @@ namespace BTCPayServer.Controllers
|
||||
public async Task<Uri> GetCallbackUriAsync(HttpRequest request)
|
||||
{
|
||||
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)
|
||||
@@ -103,12 +107,18 @@ namespace BTCPayServer.Controllers
|
||||
public async Task<Uri> GetCallbackBlockUriAsync(HttpRequest request)
|
||||
{
|
||||
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);
|
||||
return uri;
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace BTCPayServer.Controllers
|
||||
BTCPayWallet wallet,
|
||||
IRateProvider rateProvider,
|
||||
StoreRepository storeRepository,
|
||||
InvoiceWatcher watcher,
|
||||
InvoiceWatcherAccessor watcher,
|
||||
ExplorerClient explorerClient,
|
||||
IFeeProvider feeProvider)
|
||||
{
|
||||
@@ -73,7 +73,7 @@ namespace BTCPayServer.Controllers
|
||||
_InvoiceRepository = invoiceRepository ?? throw new ArgumentNullException(nameof(invoiceRepository));
|
||||
_Wallet = wallet ?? throw new ArgumentNullException(nameof(wallet));
|
||||
_RateProvider = rateProvider ?? throw new ArgumentNullException(nameof(rateProvider));
|
||||
_Watcher = watcher ?? throw new ArgumentNullException(nameof(watcher));
|
||||
_Watcher = (watcher ?? throw new ArgumentNullException(nameof(watcher))).Instance;
|
||||
_UserManager = userManager;
|
||||
_FeeProvider = feeProvider ?? throw new ArgumentNullException(nameof(feeProvider));
|
||||
}
|
||||
|
||||
@@ -71,10 +71,10 @@ namespace BTCPayServer
|
||||
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());
|
||||
return new HtmlString("var srvModel = JSON.parse('" + encodedJson + "');");
|
||||
return new HtmlString($"var {variableName} = JSON.parse('" + encodedJson + "');");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using BTCPayServer.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -34,6 +35,7 @@ using System.Threading;
|
||||
using BTCPayServer.Services.Wallets;
|
||||
using BTCPayServer.Authentication;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using BTCPayServer.Logging;
|
||||
|
||||
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)
|
||||
{
|
||||
services.AddDbContext<ApplicationDbContext>((provider, o) =>
|
||||
@@ -106,18 +95,35 @@ namespace BTCPayServer.Hosting
|
||||
services.TryAddSingleton<SettingsRepository>();
|
||||
services.TryAddSingleton<InvoicePaymentNotification>();
|
||||
services.TryAddSingleton<BTCPayServerOptions>(o => o.GetRequiredService<IOptions<BTCPayServerOptions>>().Value);
|
||||
services.TryAddSingleton<IConfigureOptions<MvcOptions>, BTCPayServerConfigureOptions>();
|
||||
services.TryAddSingleton(o =>
|
||||
services.TryAddSingleton<InvoiceRepository>(o =>
|
||||
{
|
||||
var runtime = new BTCPayServerRuntime();
|
||||
runtime.Configure(o.GetRequiredService<BTCPayServerOptions>());
|
||||
return runtime;
|
||||
var opts = o.GetRequiredService<BTCPayServerOptions>();
|
||||
var dbContext = o.GetRequiredService<ApplicationDbContextFactory>();
|
||||
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.TryAddSingleton<TokenRepository>();
|
||||
services.TryAddSingleton(o => o.GetRequiredService<BTCPayServerRuntime>().InvoiceRepository);
|
||||
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<BTCPayWallet>();
|
||||
services.TryAddSingleton<CurrencyNameTable>();
|
||||
@@ -127,10 +133,16 @@ namespace BTCPayServer.Hosting
|
||||
BlockTarget = 20,
|
||||
ExplorerClient = o.GetRequiredService<ExplorerClient>()
|
||||
});
|
||||
|
||||
services.TryAddSingleton<NBXplorerWaiterAccessor>();
|
||||
services.AddSingleton<IHostedService, NBXplorerWaiter>();
|
||||
services.TryAddSingleton<ExplorerClient>(o =>
|
||||
{
|
||||
var runtime = o.GetRequiredService<BTCPayServerRuntime>();
|
||||
return runtime.Explorer;
|
||||
var opts = o.GetRequiredService<BTCPayServerOptions>();
|
||||
var explorer = new ExplorerClient(opts.Network, opts.Explorer);
|
||||
if (!explorer.SetCookieAuth(opts.CookieFile))
|
||||
explorer.SetNoAuth();
|
||||
return explorer;
|
||||
});
|
||||
services.TryAddSingleton<Bitpay>(o =>
|
||||
{
|
||||
@@ -145,9 +157,12 @@ namespace BTCPayServer.Hosting
|
||||
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) };
|
||||
});
|
||||
services.TryAddSingleton<InvoiceWatcher>();
|
||||
|
||||
services.TryAddSingleton<InvoiceNotificationManager>();
|
||||
services.TryAddSingleton<IHostedService>(o => o.GetRequiredService<InvoiceWatcher>());
|
||||
|
||||
services.TryAddSingleton<InvoiceWatcherAccessor>();
|
||||
services.AddSingleton<IHostedService, InvoiceWatcher>();
|
||||
|
||||
services.TryAddScoped<IHttpContextAccessor, HttpContextAccessor>();
|
||||
services.TryAddSingleton<IAuthorizationHandler, OwnStoreHandler>();
|
||||
services.AddTransient<AccessTokenController>();
|
||||
@@ -174,12 +189,6 @@ namespace BTCPayServer.Hosting
|
||||
|
||||
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())
|
||||
{
|
||||
//Wait the DB is ready
|
||||
|
||||
@@ -31,71 +31,28 @@ namespace BTCPayServer.Hosting
|
||||
RequestDelegate _Next;
|
||||
CallbackController _CallbackController;
|
||||
BTCPayServerOptions _Options;
|
||||
private NBXplorerWaiterAccessor _NbxplorerAwaiter;
|
||||
|
||||
public BTCPayMiddleware(RequestDelegate next,
|
||||
TokenRepository tokenRepo,
|
||||
BTCPayServerOptions options,
|
||||
NBXplorerWaiterAccessor nbxplorerAwaiter,
|
||||
CallbackController callbackController)
|
||||
{
|
||||
_TokenRepository = tokenRepo ?? throw new ArgumentNullException(nameof(tokenRepo));
|
||||
_Next = next ?? throw new ArgumentNullException(nameof(next));
|
||||
_CallbackController = callbackController;
|
||||
_Options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
_NbxplorerAwaiter = (nbxplorerAwaiter ?? throw new ArgumentNullException(nameof(nbxplorerAwaiter)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool _Registered;
|
||||
public async Task Invoke(HttpContext httpContext)
|
||||
{
|
||||
if (!_Registered)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
RewriteHostIfNeeded(httpContext);
|
||||
await EnsureBlockCallbackRegistered(httpContext);
|
||||
|
||||
httpContext.Request.Headers.TryGetValue("x-signature", out StringValues values);
|
||||
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)
|
||||
{
|
||||
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
|
||||
{
|
||||
public class InvoiceWatcherAccessor
|
||||
{
|
||||
public InvoiceWatcher Instance { get; set; }
|
||||
}
|
||||
public class InvoiceWatcher : IHostedService
|
||||
{
|
||||
InvoiceRepository _InvoiceRepository;
|
||||
@@ -23,11 +27,13 @@ namespace BTCPayServer.Services.Invoices
|
||||
DerivationStrategyFactory _DerivationFactory;
|
||||
InvoiceNotificationManager _NotificationManager;
|
||||
BTCPayWallet _Wallet;
|
||||
|
||||
|
||||
public InvoiceWatcher(ExplorerClient explorerClient,
|
||||
InvoiceRepository invoiceRepository,
|
||||
BTCPayWallet wallet,
|
||||
InvoiceNotificationManager notificationManager)
|
||||
InvoiceNotificationManager notificationManager,
|
||||
InvoiceWatcherAccessor accessor)
|
||||
{
|
||||
LongPollingMode = explorerClient.Network == Network.RegTest;
|
||||
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);
|
||||
_InvoiceRepository = invoiceRepository ?? throw new ArgumentNullException(nameof(invoiceRepository));
|
||||
_NotificationManager = notificationManager ?? throw new ArgumentNullException(nameof(notificationManager));
|
||||
accessor.Instance = this;
|
||||
}
|
||||
|
||||
public bool LongPollingMode
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<script src="https://code.jquery.com/jquery-3.2.1.min.js"
|
||||
crossorigin="anonymous"></script>
|
||||
<script type="text/javascript">
|
||||
@Model.ToSrvModel()
|
||||
@Model.ToJSVariableModel("srvModel")
|
||||
</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>
|
||||
|
||||
@@ -78,6 +78,33 @@
|
||||
</nav>
|
||||
|
||||
@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">
|
||||
<div class="container text-right"><span style="font-size:8px;">@env.ToString()</span></div>
|
||||
</footer>
|
||||
|
||||
@@ -6,7 +6,7 @@ RUN dotnet restore
|
||||
COPY BTCPayServer/. .
|
||||
RUN dotnet publish --output /app/ --configuration Release
|
||||
|
||||
FROM microsoft/aspnetcore:2.0.0
|
||||
FROM microsoft/aspnetcore:2.0.3
|
||||
WORKDIR /app
|
||||
|
||||
RUN mkdir /datadir
|
||||
@@ -14,4 +14,4 @@ ENV BTCPAY_DATADIR=/datadir
|
||||
VOLUME /datadir
|
||||
|
||||
COPY --from=builder "/app" .
|
||||
ENTRYPOINT ["dotnet", "BTCPayServer.dll"]
|
||||
ENTRYPOINT ["dotnet", "BTCPayServer.dll"]
|
||||
|
||||
Reference in New Issue
Block a user