using System; using System.IO; using System.Linq; using System.Net; using System.Reflection; using System.Runtime.CompilerServices; using System.Threading.Tasks; using BTCPayServer.Configuration; using BTCPayServer.Hosting; using BTCPayServer.Logging; using BTCPayServer.Plugins; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting.Server.Features; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; [assembly: InternalsVisibleTo("BTCPayServer.Tests")] namespace BTCPayServer { class Program { static async Task Main(string[] args) { if (args.Length > 0 && args[0] == "run") args = args.Skip(1).ToArray(); // Hack to make dotnet watch work ServicePointManager.DefaultConnectionLimit = 100; IWebHost host = null; var processor = new ConsoleLoggerProcessor(); var loggerProvider = new CustomConsoleLogProvider(processor); using var loggerFactory = new LoggerFactory(); loggerFactory.AddProvider(loggerProvider); var logger = loggerFactory.CreateLogger("Configuration"); var logs = new Logs(); logs.Configure(loggerFactory); IConfiguration conf = null; try { var confBuilder = new DefaultConfiguration() { Logger = logger }.CreateConfigurationBuilder(args); if (confBuilder is null) return; #if DEBUG confBuilder.AddJsonFile("appsettings.dev.json", true, false); #endif conf = confBuilder.Build(); var builder = new WebHostBuilder() .UseKestrel() .UseConfiguration(conf) .ConfigureLogging(l => { l.AddFilter("Microsoft", LogLevel.Error); if (!conf.GetOrDefault("verbose", false)) l.AddFilter("Events", LogLevel.Warning); l.AddFilter("System.Net.Http.HttpClient", LogLevel.Critical); l.AddFilter("Microsoft.AspNetCore.Antiforgery.Internal", LogLevel.Critical); l.AddFilter("Fido2NetLib.DistributedCacheMetadataService", LogLevel.Error); l.AddProvider(new CustomConsoleLogProvider(processor)); }) .UseStartup(); // When we run the app with dotnet run (typically in dev env), the wwwroot isn't in the same directory // than this assembly. // But when we use dotnet publish, the wwwroot is published alongside the assembly! // This fix https://github.com/btcpayserver/btcpayserver/issues/1894 var defaultContentPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); var defaultWebRoot = Path.Combine(defaultContentPath, "wwwroot"); var defaultWebRootExists = Directory.Exists(defaultWebRoot); if (!defaultWebRootExists) { // When we use dotnet run... builder.UseContentRoot(Directory.GetCurrentDirectory()); } host = builder.Build(); await host.StartWithTasksAsync(); var urls = host.ServerFeatures.Get().Addresses; foreach (var url in urls) { // Some tools such as dotnet watch parse this exact log to open the browser logger.LogInformation("Now listening on: " + url); } await host.WaitForShutdownAsync(); } catch (ConfigException ex) { if (!string.IsNullOrEmpty(ex.Message)) logs.Configuration.LogError(ex.Message); } catch (Exception e) when (PluginManager.IsExceptionByPlugin(e, out var pluginName)) { logs.Configuration.LogError(e, $"Disabling plugin {pluginName} as it crashed on startup"); var pluginDir = new DataDirectories().Configure(conf).PluginDir; PluginManager.DisablePlugin(pluginDir, pluginName); } finally { processor.Dispose(); if (host == null) logs.Configuration.LogError("Configuration error"); host?.Dispose(); await Serilog.Log.CloseAndFlushAsync(); loggerProvider.Dispose(); } } } }