diff --git a/BTCPayServer.Abstractions/Models/DatabaseOptions.cs b/BTCPayServer.Abstractions/Models/DatabaseOptions.cs index 2c518aaa9..4f682083e 100644 --- a/BTCPayServer.Abstractions/Models/DatabaseOptions.cs +++ b/BTCPayServer.Abstractions/Models/DatabaseOptions.cs @@ -1,11 +1,41 @@ +using System; +using System.IO; +using System.Runtime.InteropServices.ComTypes; +using Microsoft.Extensions.Configuration; + namespace BTCPayServer.Abstractions.Models { public class DatabaseOptions { - public DatabaseOptions(DatabaseType type, string connString) + public DatabaseOptions(IConfiguration conf, string dataDir) { - DatabaseType = type; - ConnectionString = connString; + var postgresConnectionString = conf["postgres"]; + var mySQLConnectionString = conf["mysql"]; + var sqliteFileName = conf["sqlitefile"]; + + if (!string.IsNullOrEmpty(postgresConnectionString)) + { + DatabaseType = DatabaseType.Postgres; + ConnectionString = postgresConnectionString; + } + else if (!string.IsNullOrEmpty(mySQLConnectionString)) + { + DatabaseType = DatabaseType.MySQL; + ConnectionString = mySQLConnectionString; + } + else if (!string.IsNullOrEmpty(sqliteFileName)) + { + var connStr = "Data Source=" + (Path.IsPathRooted(sqliteFileName) + ? sqliteFileName + : Path.Combine(dataDir, sqliteFileName)); + + DatabaseType = DatabaseType.Sqlite; + ConnectionString = sqliteFileName; + } + else + { + throw new InvalidOperationException("No database option was configured."); + } } public DatabaseType DatabaseType { get; set; } diff --git a/BTCPayServer/Configuration/BTCPayServerOptions.cs b/BTCPayServer/Configuration/BTCPayServerOptions.cs index 27ea2e8fc..10bf551e4 100644 --- a/BTCPayServer/Configuration/BTCPayServerOptions.cs +++ b/BTCPayServer/Configuration/BTCPayServerOptions.cs @@ -38,11 +38,6 @@ namespace BTCPayServer.Configuration get; private set; } - public string DataDir - { - get; - private set; - } public EndPoint SocksEndpoint { get; set; } public List NBXplorerConnectionSettings @@ -64,8 +59,7 @@ namespace BTCPayServer.Configuration { if (!Path.IsPathRooted(logfile)) { - var networkType = DefaultConfiguration.GetNetworkType(configuration); - logfile = Path.Combine(configuration.GetDataDir(networkType), logfile); + logfile = Path.Combine(new DataDirectories(configuration).DataDir, logfile); } } return logfile; @@ -79,8 +73,7 @@ namespace BTCPayServer.Configuration public void LoadArgs(IConfiguration conf) { NetworkType = DefaultConfiguration.GetNetworkType(conf); - DataDir = conf.GetDataDir(NetworkType); - PluginDir = conf.GetPluginDir(NetworkType); + Logs.Configuration.LogInformation("Network: " + NetworkType.ToString()); if (conf.GetOrDefault("launchsettings", false) && NetworkType != NetworkType.Regtest) @@ -163,10 +156,6 @@ namespace BTCPayServer.Configuration OtherExternalServices.AddOrReplace(service.Name, uri); } } - - PostgresConnectionString = conf.GetOrDefault("postgres", null); - MySQLConnectionString = conf.GetOrDefault("mysql", null); - SQLiteFileName = conf.GetOrDefault("sqlitefile", null); BundleJsCss = conf.GetOrDefault("bundlejscss", true); DockerDeployment = conf.GetOrDefault("dockerdeployment", true); @@ -245,7 +234,6 @@ namespace BTCPayServer.Configuration RecommendedPlugins = conf.GetOrDefault("recommended-plugins", "").ToLowerInvariant().Split('\r','\n','\t',' ').Where(s => !string.IsNullOrEmpty(s)).Distinct().ToArray(); } - public string PluginDir { get; set; } public string PluginRemote { get; set; } public string[] RecommendedPlugins { get; set; } @@ -292,21 +280,6 @@ namespace BTCPayServer.Configuration public BTCPayNetworkProvider NetworkProvider { get; set; } public bool DockerDeployment { get; set; } - public string PostgresConnectionString - { - get; - set; - } - public string MySQLConnectionString - { - get; - set; - } - public string SQLiteFileName - { - get; - set; - } public bool BundleJsCss { get; diff --git a/BTCPayServer/Configuration/ConfigurationExtensions.cs b/BTCPayServer/Configuration/ConfigurationExtensions.cs index 8ec577f53..16c699a5a 100644 --- a/BTCPayServer/Configuration/ConfigurationExtensions.cs +++ b/BTCPayServer/Configuration/ConfigurationExtensions.cs @@ -55,23 +55,5 @@ namespace BTCPayServer.Configuration throw new NotSupportedException("Configuration value does not support time " + typeof(T).Name); } } - - public static string GetDataDir(this IConfiguration configuration) - { - var networkType = DefaultConfiguration.GetNetworkType(configuration); - return GetDataDir(configuration, networkType); - } - - public static string GetDataDir(this IConfiguration configuration, NetworkType networkType) - { - var defaultSettings = BTCPayDefaultSettings.GetDefaultSettings(networkType); - return configuration.GetOrDefault("datadir", defaultSettings.DefaultDataDirectory); - } - - public static string GetPluginDir(this IConfiguration configuration, NetworkType networkType) - { - var defaultSettings = BTCPayDefaultSettings.GetDefaultSettings(networkType); - return configuration.GetOrDefault("plugindir", defaultSettings.DefaultPluginDirectory); - } } } diff --git a/BTCPayServer/Configuration/DataDirectories.cs b/BTCPayServer/Configuration/DataDirectories.cs new file mode 100644 index 000000000..d5fa4b93d --- /dev/null +++ b/BTCPayServer/Configuration/DataDirectories.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Extensions.Configuration; + +namespace BTCPayServer.Configuration +{ + public class DataDirectories + { + public DataDirectories(IConfiguration conf) + { + var networkType = DefaultConfiguration.GetNetworkType(conf); + var defaultSettings = BTCPayDefaultSettings.GetDefaultSettings(networkType); + DataDir = conf["datadir"] ?? defaultSettings.DefaultDataDirectory; + PluginDir = conf["plugindir"] ?? defaultSettings.DefaultPluginDirectory; + StorageDir = Path.Combine(DataDir, Storage.Services.Providers.FileSystemStorage.FileSystemFileProviderService.LocalStorageDirectoryName); + TempStorageDir = Path.Combine(StorageDir, "tmp"); + } + public string DataDir { get; } + public string PluginDir { get; } + public string TempStorageDir { get; } + public string StorageDir { get; set; } + } +} diff --git a/BTCPayServer/Controllers/StorageController.cs b/BTCPayServer/Controllers/StorageController.cs index 204c0aacc..3bcd562c2 100644 --- a/BTCPayServer/Controllers/StorageController.cs +++ b/BTCPayServer/Controllers/StorageController.cs @@ -12,10 +12,10 @@ namespace BTCPayServer.Storage private readonly FileService _FileService; private readonly string _dir; - public StorageController(FileService fileService, BTCPayServerOptions serverOptions) + public StorageController(FileService fileService, DataDirectories datadirs) { _FileService = fileService; - _dir = FileSystemFileProviderService.GetTempStorageDir(serverOptions); + _dir = datadirs.TempStorageDir; } [HttpGet("{fileId}")] diff --git a/BTCPayServer/Hosting/BTCPayServerServices.cs b/BTCPayServer/Hosting/BTCPayServerServices.cs index 05553bf56..140cc9e34 100644 --- a/BTCPayServer/Hosting/BTCPayServerServices.cs +++ b/BTCPayServer/Hosting/BTCPayServerServices.cs @@ -1,5 +1,7 @@ using System; using System.IO; +using System.Runtime.InteropServices.ComTypes; +using System.Security.Cryptography; using System.Threading; using BTCPayServer.Abstractions.Contracts; using BTCPayServer.Abstractions.Extensions; @@ -65,6 +67,7 @@ namespace BTCPayServer.Hosting } public static IServiceCollection AddBTCPayServer(this IServiceCollection services, IConfiguration configuration) { + services.AddSingleton(); services.AddSingleton(o => o.GetRequiredService>().Value); services.AddDbContext((provider, o) => { @@ -98,9 +101,9 @@ namespace BTCPayServer.Hosting services.AddStartupTask(); services.TryAddSingleton(o => { - var opts = o.GetRequiredService(); + var datadirs = o.GetRequiredService(); var dbContext = o.GetRequiredService(); - var dbpath = Path.Combine(opts.DataDir, "InvoiceDB"); + var dbpath = Path.Combine(datadirs.DataDir, "InvoiceDB"); if (!Directory.Exists(dbpath)) Directory.CreateDirectory(dbpath); return new InvoiceRepository(dbContext, dbpath, o.GetRequiredService(), o.GetService()); @@ -111,32 +114,33 @@ namespace BTCPayServer.Hosting services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); + services.TryAddSingleton(); services.TryAddSingleton(o => { var opts = o.GetRequiredService(); - if (!string.IsNullOrEmpty(opts.PostgresConnectionString)) + try { - Logs.Configuration.LogInformation($"Postgres DB used"); - return new DatabaseOptions(DatabaseType.Postgres, opts.PostgresConnectionString); + var dbOptions = new DatabaseOptions(o.GetRequiredService(), + o.GetRequiredService().DataDir); + if (dbOptions.DatabaseType == DatabaseType.Postgres) + { + Logs.Configuration.LogInformation($"Postgres DB used"); + } + else if (dbOptions.DatabaseType == DatabaseType.MySQL) + { + Logs.Configuration.LogInformation($"MySQL DB used"); + Logs.Configuration.LogWarning("MySQL is not widely tested and should be considered experimental, we advise you to use postgres instead."); + } + else if (dbOptions.DatabaseType == DatabaseType.Sqlite) + { + Logs.Configuration.LogInformation($"SQLite DB used"); + Logs.Configuration.LogWarning("SQLite is not widely tested and should be considered experimental, we advise you to use postgres instead."); + } + return dbOptions; } - else if (!string.IsNullOrEmpty(opts.MySQLConnectionString)) + catch (Exception ex) { - Logs.Configuration.LogInformation($"MySQL DB used"); - Logs.Configuration.LogWarning("MySQL is not widely tested and should be considered experimental, we advise you to use postgres instead."); - return new DatabaseOptions(DatabaseType.MySQL, opts.MySQLConnectionString); - } - else if (!string.IsNullOrEmpty(opts.SQLiteFileName)) - { - var connStr = "Data Source=" +(Path.IsPathRooted(opts.SQLiteFileName) - ? opts.SQLiteFileName - : Path.Combine(opts.DataDir, opts.SQLiteFileName)); - Logs.Configuration.LogInformation($"SQLite DB used"); - Logs.Configuration.LogWarning("SQLite is not widely tested and should be considered experimental, we advise you to use postgres instead."); - return new DatabaseOptions(DatabaseType.Sqlite, connStr); - } - else - { - throw new ConfigException("No database option was configured."); + throw new ConfigException($"No database option was configured. ({ex.Message})"); } }); services.AddSingleton(); diff --git a/BTCPayServer/Hosting/Startup.cs b/BTCPayServer/Hosting/Startup.cs index 211ca2b60..7f301ea2b 100644 --- a/BTCPayServer/Hosting/Startup.cs +++ b/BTCPayServer/Hosting/Startup.cs @@ -48,7 +48,7 @@ namespace BTCPayServer.Hosting services.AddMemoryCache(); services.AddDataProtection() .SetApplicationName("BTCPay Server") - .PersistKeysToFileSystem(GetDataDir()); + .PersistKeysToFileSystem(new DirectoryInfo(new DataDirectories(Configuration).DataDir)); services.AddIdentity() .AddEntityFrameworkStores() .AddDefaultTokenProviders(); @@ -156,27 +156,23 @@ namespace BTCPayServer.Hosting IWebHostEnvironment env, IServiceProvider prov, BTCPayServerOptions options, + DataDirectories dataDirectories, ILoggerFactory loggerFactory) { Logs.Configuration.LogInformation($"Root Path: {options.RootPath}"); if (options.RootPath.Equals("/", StringComparison.OrdinalIgnoreCase)) { - ConfigureCore(app, env, prov, loggerFactory, options); + ConfigureCore(app, env, prov, loggerFactory, dataDirectories); } else { app.Map(options.RootPath, appChild => { - ConfigureCore(appChild, env, prov, loggerFactory, options); + ConfigureCore(appChild, env, prov, loggerFactory, dataDirectories); }); } } - private DirectoryInfo GetDataDir() - { - return new DirectoryInfo(Configuration.GetDataDir(DefaultConfiguration.GetNetworkType(Configuration))); - } - - private static void ConfigureCore(IApplicationBuilder app, IWebHostEnvironment env, IServiceProvider prov, ILoggerFactory loggerFactory, BTCPayServerOptions options) + private static void ConfigureCore(IApplicationBuilder app, IWebHostEnvironment env, IServiceProvider prov, ILoggerFactory loggerFactory, DataDirectories dataDirectories) { Logs.Configure(loggerFactory); app.UsePlugins(); @@ -212,7 +208,7 @@ namespace BTCPayServer.Hosting } }); - app.UseProviderStorage(options); + app.UseProviderStorage(dataDirectories); app.UseAuthentication(); app.UseAuthorization(); app.UseSession(); diff --git a/BTCPayServer/Plugins/PluginManager.cs b/BTCPayServer/Plugins/PluginManager.cs index 264c6d231..de9c15292 100644 --- a/BTCPayServer/Plugins/PluginManager.cs +++ b/BTCPayServer/Plugins/PluginManager.cs @@ -5,6 +5,7 @@ using System.IO; using System.IO.Compression; using System.Linq; using System.Reflection; +using System.Runtime.InteropServices.ComTypes; using BTCPayServer.Abstractions.Contracts; using BTCPayServer.Configuration; using McMaster.NETCore.Plugins; @@ -27,7 +28,7 @@ namespace BTCPayServer.Plugins IConfiguration config, ILoggerFactory loggerFactory) { _logger = loggerFactory.CreateLogger(typeof(PluginManager)); - var pluginsFolder = config.GetPluginDir(DefaultConfiguration.GetNetworkType(config)); + var pluginsFolder = new DataDirectories(config).PluginDir; var plugins = new List(); diff --git a/BTCPayServer/Plugins/PluginService.cs b/BTCPayServer/Plugins/PluginService.cs index 7eec0e369..8e264294b 100644 --- a/BTCPayServer/Plugins/PluginService.cs +++ b/BTCPayServer/Plugins/PluginService.cs @@ -17,17 +17,19 @@ namespace BTCPayServer.Plugins { public class PluginService: IPluginHookService { - private readonly BTCPayServerOptions _btcPayServerOptions; + private readonly DataDirectories _datadirs; + private readonly BTCPayServerOptions _options; private readonly HttpClient _githubClient; private readonly IEnumerable _actions; private readonly IEnumerable _filters; public PluginService(IEnumerable btcPayServerPlugins, - IHttpClientFactory httpClientFactory, BTCPayServerOptions btcPayServerOptions,IEnumerable actions, IEnumerable filters) + IHttpClientFactory httpClientFactory, DataDirectories datadirs, BTCPayServerOptions options, IEnumerable actions, IEnumerable filters) { LoadedPlugins = btcPayServerPlugins; _githubClient = httpClientFactory.CreateClient(); _githubClient.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("btcpayserver", "1")); - _btcPayServerOptions = btcPayServerOptions; + _datadirs = datadirs; + _options = options; _actions = actions; _filters = filters; } @@ -37,7 +39,7 @@ namespace BTCPayServer.Plugins public async Task> GetRemotePlugins() { var resp = await _githubClient - .GetStringAsync(new Uri($"https://api.github.com/repos/{_btcPayServerOptions.PluginRemote}/contents")); + .GetStringAsync(new Uri($"https://api.github.com/repos/{_options.PluginRemote}/contents")); var files = JsonConvert.DeserializeObject(resp); return await Task.WhenAll(files.Where(file => file.Name.EndsWith($"{PluginManager.BTCPayPluginSuffix}.json", StringComparison.InvariantCulture)).Select(async file => { @@ -48,9 +50,9 @@ namespace BTCPayServer.Plugins public async Task DownloadRemotePlugin(string plugin) { - var dest = _btcPayServerOptions.PluginDir; + var dest = _datadirs.PluginDir; var resp = await _githubClient - .GetStringAsync(new Uri($"https://api.github.com/repos/{_btcPayServerOptions.PluginRemote}/contents")); + .GetStringAsync(new Uri($"https://api.github.com/repos/{_options.PluginRemote}/contents")); var files = JsonConvert.DeserializeObject(resp); var ext = files.SingleOrDefault(file => file.Name == $"{plugin}{PluginManager.BTCPayPluginSuffix}"); if (ext is null) @@ -65,19 +67,19 @@ namespace BTCPayServer.Plugins public void InstallPlugin(string plugin) { - var dest = _btcPayServerOptions.PluginDir; + var dest = _datadirs.PluginDir; UninstallPlugin(plugin); PluginManager.QueueCommands(dest, ("install", plugin)); } public void UpdatePlugin(string plugin) { - var dest = _btcPayServerOptions.PluginDir; + var dest = _datadirs.PluginDir; PluginManager.QueueCommands(dest, ("update", plugin)); } public async Task UploadPlugin(IFormFile plugin) { - var dest = _btcPayServerOptions.PluginDir; + var dest = _datadirs.PluginDir; var filedest = Path.Combine(dest, plugin.FileName); Directory.CreateDirectory(Path.GetDirectoryName(filedest)); if (Path.GetExtension(filedest) == PluginManager.BTCPayPluginSuffix) @@ -89,7 +91,7 @@ namespace BTCPayServer.Plugins public void UninstallPlugin(string plugin) { - var dest = _btcPayServerOptions.PluginDir; + var dest = _datadirs.PluginDir; PluginManager.QueueCommands(dest, ("delete", plugin)); } @@ -124,12 +126,12 @@ namespace BTCPayServer.Plugins public (string command, string plugin)[] GetPendingCommands() { - return PluginManager.GetPendingCommands(_btcPayServerOptions.PluginDir); + return PluginManager.GetPendingCommands(_datadirs.PluginDir); } public void CancelCommands(string plugin) { - PluginManager.CancelCommands(_btcPayServerOptions.PluginDir, plugin); + PluginManager.CancelCommands(_datadirs.PluginDir, plugin); } public async Task ApplyAction(string hook, object args) diff --git a/BTCPayServer/Storage/Services/Providers/FileSystemStorage/FileSystemFileProviderService.cs b/BTCPayServer/Storage/Services/Providers/FileSystemStorage/FileSystemFileProviderService.cs index 10d5a94f1..0f795827a 100644 --- a/BTCPayServer/Storage/Services/Providers/FileSystemStorage/FileSystemFileProviderService.cs +++ b/BTCPayServer/Storage/Services/Providers/FileSystemStorage/FileSystemFileProviderService.cs @@ -14,24 +14,14 @@ namespace BTCPayServer.Storage.Services.Providers.FileSystemStorage public class FileSystemFileProviderService : BaseTwentyTwentyStorageFileProviderServiceBase { - private readonly BTCPayServerOptions _options; + private readonly DataDirectories _datadirs; - public FileSystemFileProviderService(BTCPayServerOptions options) + public FileSystemFileProviderService(DataDirectories datadirs) { - _options = options; + _datadirs = datadirs; } public const string LocalStorageDirectoryName = "LocalStorage"; - public static string GetStorageDir(BTCPayServerOptions options) - { - return Path.Combine(options.DataDir, LocalStorageDirectoryName); - } - - - public static string GetTempStorageDir(BTCPayServerOptions options) - { - return Path.Combine(GetStorageDir(options), "tmp"); - } public override StorageProvider StorageProvider() { return Storage.Models.StorageProvider.FileSystem; @@ -40,14 +30,14 @@ namespace BTCPayServer.Storage.Services.Providers.FileSystemStorage protected override Task GetStorageProvider(FileSystemStorageConfiguration configuration) { return Task.FromResult( - new LocalStorageProvider(new DirectoryInfo(GetStorageDir(_options)).FullName)); + new LocalStorageProvider(new DirectoryInfo(_datadirs.StorageDir).FullName)); } public override async Task GetFileUrl(Uri baseUri, StoredFile storedFile, StorageSettings configuration) { var baseResult = await base.GetFileUrl(baseUri, storedFile, configuration); var url = new Uri(baseUri, LocalStorageDirectoryName); - return baseResult.Replace(new DirectoryInfo(GetStorageDir(_options)).FullName, url.AbsoluteUri, + return baseResult.Replace(new DirectoryInfo(_datadirs.StorageDir).FullName, url.AbsoluteUri, StringComparison.InvariantCultureIgnoreCase); } @@ -63,13 +53,13 @@ namespace BTCPayServer.Storage.Services.Providers.FileSystemStorage IsDownload = isDownload }; var name = Guid.NewGuid().ToString(); - var fullPath = Path.Combine(GetTempStorageDir(_options), name); + var fullPath = Path.Combine(_datadirs.TempStorageDir, name); if (!File.Exists(fullPath)) { File.Create(fullPath).Dispose(); } - await File.WriteAllTextAsync(Path.Combine(GetTempStorageDir(_options), name), JsonConvert.SerializeObject(localFileDescriptor)); + await File.WriteAllTextAsync(Path.Combine(_datadirs.TempStorageDir, name), JsonConvert.SerializeObject(localFileDescriptor)); return new Uri(baseUri, $"{LocalStorageDirectoryName}tmp/{name}{(isDownload ? "?download" : string.Empty)}").AbsoluteUri; } diff --git a/BTCPayServer/Storage/StorageExtensions.cs b/BTCPayServer/Storage/StorageExtensions.cs index 50e256fd7..fda11eb9a 100644 --- a/BTCPayServer/Storage/StorageExtensions.cs +++ b/BTCPayServer/Storage/StorageExtensions.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Runtime.InteropServices.ComTypes; using BTCPayServer.Configuration; using BTCPayServer.Storage.Services; using BTCPayServer.Storage.Services.Providers; @@ -27,30 +28,28 @@ namespace BTCPayServer.Storage // serviceCollection.AddSingleton(); } - public static void UseProviderStorage(this IApplicationBuilder builder, BTCPayServerOptions options) + public static void UseProviderStorage(this IApplicationBuilder builder, DataDirectories datadirs) { try { - var dir = FileSystemFileProviderService.GetStorageDir(options); - var tmpdir = FileSystemFileProviderService.GetTempStorageDir(options); DirectoryInfo dirInfo; - if (!Directory.Exists(dir)) + if (!Directory.Exists(datadirs.StorageDir)) { - dirInfo = Directory.CreateDirectory(dir); + dirInfo = Directory.CreateDirectory(datadirs.StorageDir); } else { - dirInfo = new DirectoryInfo(dir); + dirInfo = new DirectoryInfo(datadirs.StorageDir); } DirectoryInfo tmpdirInfo; - if (!Directory.Exists(tmpdir)) + if (!Directory.Exists(datadirs.TempStorageDir)) { - tmpdirInfo = Directory.CreateDirectory(tmpdir); + tmpdirInfo = Directory.CreateDirectory(datadirs.TempStorageDir); } else { - tmpdirInfo = new DirectoryInfo(tmpdir); + tmpdirInfo = new DirectoryInfo(datadirs.TempStorageDir); } builder.UseStaticFiles(new StaticFileOptions()