diff --git a/Oqtane.Client/Installer/Installer.razor b/Oqtane.Client/Installer/Installer.razor index 019e12d5..968c582e 100644 --- a/Oqtane.Client/Installer/Installer.razor +++ b/Oqtane.Client/Installer/Installer.razor @@ -31,7 +31,7 @@ { foreach (var database in _databases) { - + } } @@ -174,7 +174,6 @@ var config = new InstallConfig { DatabaseType = database.DBType, - DatabasePackage = database.Package, ConnectionString = connectionString, Aliases = uri.Authority, HostEmail = _hostEmail, diff --git a/Oqtane.Client/Modules/Admin/Sites/Add.razor b/Oqtane.Client/Modules/Admin/Sites/Add.razor index 9a93ab17..832f1b33 100644 --- a/Oqtane.Client/Modules/Admin/Sites/Add.razor +++ b/Oqtane.Client/Modules/Admin/Sites/Add.razor @@ -130,7 +130,7 @@ else @@ -305,7 +305,6 @@ else { config.TenantName = _tenantName; config.DatabaseType = database.DBType; - config.DatabasePackage = database.Package; config.ConnectionString = connectionString; config.HostEmail = user.Email; config.HostPassword = _hostpassword; diff --git a/Oqtane.Client/Services/DatabaseService.cs b/Oqtane.Client/Services/DatabaseService.cs index 5f9aa925..81c93248 100644 --- a/Oqtane.Client/Services/DatabaseService.cs +++ b/Oqtane.Client/Services/DatabaseService.cs @@ -24,7 +24,7 @@ namespace Oqtane.Services public async Task> GetDatabasesAsync() { List databases = await GetJsonAsync>(Apiurl); - return databases.OrderBy(item => item.FriendlyName).ToList(); + return databases.OrderBy(item => item.Name).ToList(); } } } diff --git a/Oqtane.Server/Extensions/DbContextOptionsBuilderExtensions.cs b/Oqtane.Server/Extensions/DbContextOptionsBuilderExtensions.cs index fe7cd083..a35ecf77 100644 --- a/Oqtane.Server/Extensions/DbContextOptionsBuilderExtensions.cs +++ b/Oqtane.Server/Extensions/DbContextOptionsBuilderExtensions.cs @@ -1,8 +1,6 @@ -using System; using System.Diagnostics.CodeAnalysis; using Microsoft.EntityFrameworkCore; using Oqtane.Databases.Interfaces; -using Oqtane.Interfaces; // ReSharper disable ConvertToUsingDeclaration namespace Oqtane.Extensions diff --git a/Oqtane.Server/Infrastructure/ConfigManager.cs b/Oqtane.Server/Infrastructure/ConfigManager.cs new file mode 100644 index 00000000..196b2087 --- /dev/null +++ b/Oqtane.Server/Infrastructure/ConfigManager.cs @@ -0,0 +1,102 @@ +using System; +using System.IO; +using Microsoft.Extensions.Configuration; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Oqtane.Infrastructure +{ + public class ConfigManager : IConfigManager + { + private readonly IConfigurationRoot _config; + + public ConfigManager(IConfigurationRoot config) + { + _config = config; + } + + public IConfigurationSection GetSection(string key) + { + return _config.GetSection(key); + } + + public string GetSetting(string sectionKey, string settingKey, string defaultValue) + { + var value = _config.GetSection(sectionKey).GetValue(settingKey, defaultValue); + if (string.IsNullOrEmpty(value)) value = defaultValue; + return value; + } + + public void AddOrUpdateSetting(string key, T value, bool reload) + { + AddOrUpdateSetting("appsettings.json", key, value, reload); + } + + public void AddOrUpdateSetting(string file, string key, T value, bool reload) + { + try + { + var path = Path.Combine(Directory.GetCurrentDirectory(), file); + dynamic jsonObj = JsonConvert.DeserializeObject(File.ReadAllText(path)); + SetValueRecursively(key, jsonObj, value, "set"); + File.WriteAllText(path, JsonConvert.SerializeObject(jsonObj, Formatting.Indented)); + if (reload) Reload(); + } + catch (Exception ex) + { + Console.WriteLine("Error modifying app settings {0}", ex); + } + } + + public void RemoveSetting(string key, bool reload) + { + RemoveSetting("appsettings.json", key, reload); + } + + public void RemoveSetting(string file, string key, bool reload) + { + try + { + var path = Path.Combine(Directory.GetCurrentDirectory(), file); + dynamic jsonObj = JsonConvert.DeserializeObject(File.ReadAllText(path)); + SetValueRecursively(key, jsonObj, "", "remove"); + File.WriteAllText(path, JsonConvert.SerializeObject(jsonObj, Formatting.Indented)); + if (reload) Reload(); + } + catch (Exception ex) + { + Console.WriteLine("Error modifying app settings {0}", ex); + } + } + + private void SetValueRecursively(string key, dynamic jsonObj, T value, string action) + { + var remainingSections = key.Split(":", 2); + + var currentSection = remainingSections[0]; + if (remainingSections.Length > 1) + { + var nextSection = remainingSections[1]; + jsonObj[currentSection] ??= new JObject(); + SetValueRecursively(nextSection, jsonObj[currentSection], value, action); + } + else + { + switch (action) + { + case "set": + jsonObj[currentSection] = value; + break; + case "remove": + jsonObj.Property(currentSection).Remove(); + break; + } + } + } + + public void Reload() + { + _config.Reload(); + } + } +} diff --git a/Oqtane.Server/Infrastructure/DatabaseManager.cs b/Oqtane.Server/Infrastructure/DatabaseManager.cs index 986aa4ca..a7030c6a 100644 --- a/Oqtane.Server/Infrastructure/DatabaseManager.cs +++ b/Oqtane.Server/Infrastructure/DatabaseManager.cs @@ -10,14 +10,13 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Newtonsoft.Json; using Oqtane.Databases.Interfaces; using Oqtane.Extensions; using Oqtane.Models; using Oqtane.Repository; using Oqtane.Shared; using Oqtane.Enums; -using File = System.IO.File; +using Newtonsoft.Json; // ReSharper disable MemberCanBePrivate.Global // ReSharper disable ConvertToUsingDeclaration @@ -32,18 +31,23 @@ namespace Oqtane.Infrastructure private readonly IServiceScopeFactory _serviceScopeFactory; private readonly IWebHostEnvironment _environment; private readonly IMemoryCache _cache; + private readonly IConfigManager _configManager; - public DatabaseManager(IConfigurationRoot config, IServiceScopeFactory serviceScopeFactory, IWebHostEnvironment environment, IMemoryCache cache) + public DatabaseManager(IConfigurationRoot config, IServiceScopeFactory serviceScopeFactory, IWebHostEnvironment environment, IMemoryCache cache, IConfigManager configManager) { _config = config; _serviceScopeFactory = serviceScopeFactory; _environment = environment; _cache = cache; + _configManager = configManager; } public Installation IsInstalled() { var result = new Installation { Success = false, Message = string.Empty }; + + ValidateConfiguration(); + if (!string.IsNullOrEmpty(_config.GetConnectionString(SettingKeys.ConnectionStringKey))) { result.Success = true; @@ -97,7 +101,6 @@ namespace Oqtane.Infrastructure if (string.IsNullOrEmpty(install.DatabaseType)) { install.DatabaseType = Constants.DefaultDBType; - install.DatabasePackage = Constants.DefaultDBType.Substring(Constants.DefaultDBType.IndexOf(",") + 2); InstallDatabase(install); UpdateDatabaseType(install.DatabaseType); } @@ -106,7 +109,6 @@ namespace Oqtane.Infrastructure // if database type does not exist, install the associated Nuget package if (Type.GetType(install.DatabaseType) == null) { - install.DatabasePackage = install.DatabaseType.Substring(install.DatabaseType.IndexOf(",") + 2); InstallDatabase(install); } } @@ -220,7 +222,7 @@ namespace Oqtane.Infrastructure // iterate through Nuget packages in source folder foreach (var package in packagesFolder.GetFiles("*.nupkg.bak")) { - if (package.Name.StartsWith(install.DatabasePackage)) + if (package.Name.StartsWith(Utilities.GetAssemblyName(install.DatabaseType))) { //rename file var packageName = Path.Combine(package.DirectoryName, package.Name); @@ -237,7 +239,7 @@ namespace Oqtane.Infrastructure var assemblyPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location); var assembliesFolder = new DirectoryInfo(assemblyPath); - var assemblyFile = new FileInfo($"{assembliesFolder}/{install.DatabasePackage}.dll"); + var assemblyFile = new FileInfo($"{assembliesFolder}/{Utilities.GetAssemblyName(install.DatabaseType)}.dll"); AssemblyLoadContext.Default.LoadOqtaneAssembly(assemblyFile); @@ -644,25 +646,6 @@ namespace Oqtane.Infrastructure return result; } - public void AddOrUpdateAppSetting(string sectionPathKey, T value) - { - try - { - var filePath = Path.Combine(Directory.GetCurrentDirectory(), "appsettings.json"); - var json = File.ReadAllText(filePath); - dynamic jsonObj = JsonConvert.DeserializeObject(json); - - SetValueRecursively(sectionPathKey, jsonObj, value); - - string output = JsonConvert.SerializeObject(jsonObj, Formatting.Indented); - File.WriteAllText(filePath, output); - } - catch (Exception ex) - { - Console.WriteLine("Error writing app settings | {0}", ex); - } - } - private string DenormalizeConnectionString(string connectionString) { var dataDirectory = AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString(); @@ -687,10 +670,7 @@ namespace Oqtane.Infrastructure private string GetInstallationConfig(string key, string defaultValue) { - var value = _config.GetSection(SettingKeys.InstallationSection).GetValue(key, defaultValue); - // double fallback to default value - allow hold sample keys in config - if (string.IsNullOrEmpty(value)) value = defaultValue; - return value; + return _configManager.GetSetting(SettingKeys.InstallationSection, key, defaultValue); } private string NormalizeConnectionString(string connectionString) @@ -700,39 +680,18 @@ namespace Oqtane.Infrastructure return connectionString; } - private void SetValueRecursively(string sectionPathKey, dynamic jsonObj, T value) - { - // split the string at the first ':' character - var remainingSections = sectionPathKey.Split(":", 2); - - var currentSection = remainingSections[0]; - if (remainingSections.Length > 1) - { - // continue with the process, moving down the tree - var nextSection = remainingSections[1]; - SetValueRecursively(nextSection, jsonObj[currentSection], value); - } - else - { - // we've got to the end of the tree, set the value - jsonObj[currentSection] = value; - } - } - public void UpdateConnectionString(string connectionString) { connectionString = DenormalizeConnectionString(connectionString); if (_config.GetConnectionString(SettingKeys.ConnectionStringKey) != connectionString) { - AddOrUpdateAppSetting($"{SettingKeys.ConnectionStringsSection}:{SettingKeys.ConnectionStringKey}", connectionString); - _config.Reload(); + _configManager.AddOrUpdateSetting($"{SettingKeys.ConnectionStringsSection}:{SettingKeys.ConnectionStringKey}", connectionString, true); } } public void UpdateDatabaseType(string databaseType) { - AddOrUpdateAppSetting($"{SettingKeys.DatabaseSection}:{SettingKeys.DatabaseTypeKey}", databaseType); - _config.Reload(); + _configManager.AddOrUpdateSetting($"{SettingKeys.DatabaseSection}:{SettingKeys.DatabaseTypeKey}", databaseType, true); } public void UpgradeSqlServer(ISqlRepository sql, string connectionString, string databaseType, bool isMaster) @@ -744,5 +703,24 @@ namespace Oqtane.Infrastructure sql.ExecuteNonQuery(connectionString, databaseType, query); } + + private void ValidateConfiguration() + { + if (_configManager.GetSetting(SettingKeys.DatabaseSection, SettingKeys.DatabaseTypeKey, "") == "") + { + _configManager.AddOrUpdateSetting($"{SettingKeys.DatabaseSection}:{SettingKeys.DatabaseTypeKey}", Constants.DefaultDBType, true); + } + if (!_configManager.GetSection(SettingKeys.AvailableDatabasesSection).Exists()) + { + string databases = "["; + databases += "{ \"Name\": \"LocalDB\", \"ControlType\": \"Oqtane.Installer.Controls.LocalDBConfig, Oqtane.Client\", \"DBTYpe\": \"Oqtane.Database.SqlServer.SqlServerDatabase, Oqtane.Database.SqlServer\" },"; + databases += "{ \"Name\": \"SQL Server\", \"ControlType\": \"Oqtane.Installer.Controls.SqlServerConfig, Oqtane.Client\", \"DBTYpe\": \"Oqtane.Database.SqlServer.SqlServerDatabase, Oqtane.Database.SqlServer\" },"; + databases += "{ \"Name\": \"SQLite\", \"ControlType\": \"Oqtane.Installer.Controls.SqliteConfig, Oqtane.Client\", \"DBTYpe\": \"Oqtane.Database.Sqlite.SqliteDatabase, Oqtane.Database.Sqlite\" },"; + databases += "{ \"Name\": \"MySQL\", \"ControlType\": \"Oqtane.Installer.Controls.MySQLConfig, Oqtane.Client\", \"DBTYpe\": \"Oqtane.Database.MySQL.SqlServerDatabase, Oqtane.Database.MySQL\" },"; + databases += "{ \"Name\": \"PostgreSQL\", \"ControlType\": \"Oqtane.Installer.Controls.PostgreSQLConfig, Oqtane.Client\", \"DBTYpe\": \"Oqtane.Database.PostgreSQL.PostgreSQLDatabase, Oqtane.Database.PostgreSQL\" }"; + databases += "]"; + _configManager.AddOrUpdateSetting(SettingKeys.AvailableDatabasesSection, JsonConvert.DeserializeObject(databases), true); + } + } } } diff --git a/Oqtane.Server/Infrastructure/Interfaces/IConfigManager.cs b/Oqtane.Server/Infrastructure/Interfaces/IConfigManager.cs new file mode 100644 index 00000000..cfc454b2 --- /dev/null +++ b/Oqtane.Server/Infrastructure/Interfaces/IConfigManager.cs @@ -0,0 +1,15 @@ +using Microsoft.Extensions.Configuration; + +namespace Oqtane.Infrastructure +{ + public interface IConfigManager + { + public IConfigurationSection GetSection(string sectionKey); + public string GetSetting(string sectionKey, string settingKey, string defaultValue); + void AddOrUpdateSetting(string key, T value, bool reload); + void AddOrUpdateSetting(string file, string key, T value, bool reload); + void RemoveSetting(string key, bool reload); + void RemoveSetting(string file, string key, bool reload); + void Reload(); + } +} diff --git a/Oqtane.Server/Infrastructure/UpgradeManager.cs b/Oqtane.Server/Infrastructure/UpgradeManager.cs index 184abd3d..dd806e3a 100644 --- a/Oqtane.Server/Infrastructure/UpgradeManager.cs +++ b/Oqtane.Server/Infrastructure/UpgradeManager.cs @@ -1,5 +1,7 @@ using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Newtonsoft.Json; using Oqtane.Models; using Oqtane.Repository; using Oqtane.Shared; @@ -11,15 +13,15 @@ namespace Oqtane.Infrastructure { public class UpgradeManager : IUpgradeManager { - private readonly IAliasRepository _aliases; private readonly IServiceScopeFactory _serviceScopeFactory; private readonly IWebHostEnvironment _environment; + private readonly IConfigManager _configManager; - public UpgradeManager(IAliasRepository aliases, IServiceScopeFactory serviceScopeFactory, IWebHostEnvironment environment) + public UpgradeManager(IServiceScopeFactory serviceScopeFactory, IWebHostEnvironment environment, IConfigManager configManager) { - _aliases = aliases; _serviceScopeFactory = serviceScopeFactory; _environment = environment; + _configManager = configManager; } public void Upgrade(Tenant tenant, string version) diff --git a/Oqtane.Server/Startup.cs b/Oqtane.Server/Startup.cs index 0a1dd1cd..061b78bc 100644 --- a/Oqtane.Server/Startup.cs +++ b/Oqtane.Server/Startup.cs @@ -58,7 +58,7 @@ namespace Oqtane // Register localization services services.AddLocalization(options => options.ResourcesPath = "Resources"); - services.AddOptions>().Bind(Configuration.GetSection("AvailableDatabases")); + services.AddOptions>().Bind(Configuration.GetSection(SettingKeys.AvailableDatabasesSection)); services.AddServerSideBlazor().AddCircuitOptions(options => { @@ -176,6 +176,7 @@ namespace Oqtane services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); // install any modules or themes ( this needs to occur BEFORE the assemblies are loaded into the app domain ) InstallationManager.InstallPackages(_env.WebRootPath, _env.ContentRootPath); diff --git a/Oqtane.Shared/Models/Database.cs b/Oqtane.Shared/Models/Database.cs index ebce1d34..334aaddb 100644 --- a/Oqtane.Shared/Models/Database.cs +++ b/Oqtane.Shared/Models/Database.cs @@ -5,11 +5,6 @@ namespace Oqtane.Models /// public class Database { - /// - /// Friendly name for the Admin to identify the DB - /// - public string FriendlyName { get; set; } - /// /// Name of the Database /// @@ -24,10 +19,5 @@ namespace Oqtane.Models /// Type of DB using the full namespace, like `Oqtane.Database.SqlServer.SqlServerDatabase, Oqtane.Database.SqlServer` /// public string DBType { get; set; } - - /// - /// Software package responsible for using this DB - like `Oqtane.Database.MySQL` - /// - public string Package { get; set; } } } diff --git a/Oqtane.Shared/Shared/InstallConfig.cs b/Oqtane.Shared/Shared/InstallConfig.cs index 7848c522..358e6465 100644 --- a/Oqtane.Shared/Shared/InstallConfig.cs +++ b/Oqtane.Shared/Shared/InstallConfig.cs @@ -5,7 +5,6 @@ namespace Oqtane.Shared { public string ConnectionString { get; set; } public string DatabaseType { get; set; } - public string DatabasePackage { get; set; } public string Aliases { get; set; } public string TenantName { get; set; } public bool IsNewTenant { get; set; } diff --git a/Oqtane.Shared/Shared/SettingKeys.cs b/Oqtane.Shared/Shared/SettingKeys.cs index 3aa66505..092a347c 100644 --- a/Oqtane.Shared/Shared/SettingKeys.cs +++ b/Oqtane.Shared/Shared/SettingKeys.cs @@ -16,5 +16,7 @@ namespace Oqtane.Shared public const string DefaultThemeKey = "DefaultTheme"; public const string DefaultLayoutKey = "DefaultLayout"; public const string DefaultContainerKey = "DefaultContainer"; + + public const string AvailableDatabasesSection = "AvailableDatabases"; } }