using BTCPayServer.Configuration; using Microsoft.Extensions.Logging; using Microsoft.AspNetCore.Hosting; using System; using System.Collections.Generic; using System.Text; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.AspNetCore.Http; using NBitpayClient; using NBitcoin; using BTCPayServer.Data; using Microsoft.EntityFrameworkCore; using System.IO; using NBXplorer; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Hosting; using BTCPayServer.Services; using BTCPayServer.Services.Invoices; using BTCPayServer.Services.Rates; using BTCPayServer.Services.Stores; using BTCPayServer.Services.Fees; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Rewrite; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Options; using Microsoft.AspNetCore.Authorization; using BTCPayServer.Controllers; using BTCPayServer.Services.Mails; using Microsoft.AspNetCore.Identity; using BTCPayServer.Models; using System.Threading.Tasks; using System.Threading; using BTCPayServer.Services.Wallets; using BTCPayServer.Authentication; using Microsoft.Extensions.Caching.Memory; using BTCPayServer.Logging; using BTCPayServer.HostedServices; using Meziantou.AspNetCore.BundleTagHelpers; using System.Security.Claims; using BTCPayServer.Hubs; using BTCPayServer.Payments.Changelly; using BTCPayServer.Payments.Lightning; using BTCPayServer.Security; using Microsoft.AspNetCore.Mvc.ModelBinding; using NBXplorer.DerivationStrategy; using NicolasDorier.RateLimits; using Npgsql; namespace BTCPayServer.Hosting { public static class BTCPayServerServices { public static IServiceCollection AddBTCPayServer(this IServiceCollection services) { services.AddDbContext((provider, o) => { var factory = provider.GetRequiredService(); factory.ConfigureBuilder(o); }); services.AddHttpClient(); services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(o => o.GetRequiredService>().Value); services.TryAddSingleton(o => { var opts = o.GetRequiredService(); var dbContext = o.GetRequiredService(); var dbpath = Path.Combine(opts.DataDir, "InvoiceDB"); if (!Directory.Exists(dbpath)) Directory.CreateDirectory(dbpath); return new InvoiceRepository(dbContext, dbpath); }); services.AddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(o => { var opts = o.GetRequiredService(); ApplicationDbContextFactory dbContext = null; if (!String.IsNullOrEmpty(opts.PostgresConnectionString)) { Logs.Configuration.LogInformation($"Postgres DB used ({opts.PostgresConnectionString})"); dbContext = new ApplicationDbContextFactory(DatabaseType.Postgres, opts.PostgresConnectionString); } else if(!String.IsNullOrEmpty(opts.MySQLConnectionString)) { Logs.Configuration.LogInformation($"MySQL DB used ({opts.MySQLConnectionString})"); dbContext = new ApplicationDbContextFactory(DatabaseType.MySQL, opts.MySQLConnectionString); } else { var connStr = "Data Source=" + Path.Combine(opts.DataDir, "sqllite.db"); Logs.Configuration.LogInformation($"SQLite DB used ({connStr})"); dbContext = new ApplicationDbContextFactory(DatabaseType.Sqlite, connStr); } return dbContext; }); services.TryAddSingleton(o => { var opts = o.GetRequiredService(); return opts.NetworkProvider; }); services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(o => new NBXplorerFeeProviderFactory(o.GetRequiredService()) { Fallback = new FeeRate(100, 1), BlockTarget = 20 }); services.AddSingleton(); services.Configure((o) => { o.Filters.Add(new ContentSecurityPolicyCssThemeManager()); o.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(WalletId))); o.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(DerivationStrategyBase))); }); services.AddSingleton(); services.AddSingleton(); services.AddSingleton, Payments.Bitcoin.BitcoinLikePaymentHandler>(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton, Payments.Lightning.LightningLikePaymentHandler>(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddTransient, BTCPayClaimsFilter>(); services.TryAddSingleton(); services.TryAddSingleton(o => { if (o.GetRequiredService().NetworkType == NetworkType.Mainnet) return new Bitpay(new Key(), new Uri("https://bitpay.com/")); else return new Bitpay(new Key(), new Uri("https://test.bitpay.com/")); }); services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddScoped(); services.AddTransient(); services.AddTransient(); services.AddTransient(); // Add application services. services.AddTransient(); // bundling services.AddAuthorization(o => Policies.AddBTCPayPolicies(o)); BitpayAuthentication.AddAuthentication(services); services.AddBundles(); services.AddTransient(provider => { var opts = provider.GetRequiredService(); var bundle = new BundleOptions(); bundle.UseBundles = opts.BundleJsCss; bundle.AppendVersion = true; return bundle; }); services.AddCors(options=> { options.AddPolicy(CorsPolicies.All, p=>p.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin()); }); var rateLimits = new RateLimitService(); rateLimits.SetZone($"zone={ZoneLimits.Login} rate=5r/min burst=3 nodelay"); services.AddSingleton(rateLimits); return services; } public static IApplicationBuilder UsePayServer(this IApplicationBuilder app) { using (var scope = app.ApplicationServices.GetService().CreateScope()) { //Wait the DB is ready Retry(() => { scope.ServiceProvider.GetRequiredService().Database.Migrate(); }); } app.UseMiddleware(); return app; } static void Retry(Action act) { CancellationTokenSource cts = new CancellationTokenSource(1000); while (true) { try { act(); return; } // Starting up catch (PostgresException ex) when (ex.SqlState == "57P03") { Thread.Sleep(1000); } catch when (!cts.IsCancellationRequested) { Thread.Sleep(100); } } } } }