diff --git a/Oqtane.Client/Modules/Admin/Sites/Add.razor b/Oqtane.Client/Modules/Admin/Sites/Add.razor index 7700a3b0..2afb5ed4 100644 --- a/Oqtane.Client/Modules/Admin/Sites/Add.razor +++ b/Oqtane.Client/Modules/Admin/Sites/Add.razor @@ -164,7 +164,7 @@ else _tenantid = (string)e.Value; if (_tenantid != "-1") { - Tenant tenant = _tenants.Where(item => item.TenantId == int.Parse(_tenantid)).FirstOrDefault(); + Tenant tenant = _tenants.FirstOrDefault(item => item.TenantId == int.Parse(_tenantid)); if (tenant != null) { _isinitialized = tenant.IsInitialized; @@ -273,8 +273,11 @@ else if (user != null) { Tenant tenant = _tenants.FirstOrDefault(item => item.TenantId == int.Parse(_tenantid)); - tenant.IsInitialized = true; - await TenantService.UpdateTenantAsync(tenant); + if (tenant != null) + { + tenant.IsInitialized = true; + await TenantService.UpdateTenantAsync(tenant); + } } } await Log(aliases[0], LogLevel.Information, "", null, "Site Created {Site}", site); diff --git a/Oqtane.Client/Modules/Admin/Tenants/Add.razor b/Oqtane.Client/Modules/Admin/Tenants/Add.razor index 4348bda7..9fe32192 100644 --- a/Oqtane.Client/Modules/Admin/Tenants/Add.razor +++ b/Oqtane.Client/Modules/Admin/Tenants/Add.razor @@ -67,14 +67,6 @@ - - - - - - - - Cancel @@ -88,7 +80,6 @@ string database = "Oqtane-" + DateTime.UtcNow.ToString("yyyyMMddHHmm"); string username = ""; string password = ""; - string schema = ""; string integratedsecurity = "display: none;"; private void SetIntegratedSecurity(ChangeEventArgs e) @@ -109,32 +100,41 @@ { ShowProgressIndicator(); - string connectionstring = ""; + string connectionString = ""; if (type == "LocalDB") { - connectionstring = "Data Source=" + server + ";AttachDbFilename=|DataDirectory|\\" + database + ".mdf;Initial Catalog=" + database + ";Integrated Security=SSPI;"; + connectionString = "Data Source=" + server + ";AttachDbFilename=|DataDirectory|\\" + database + ".mdf;Initial Catalog=" + database + ";Integrated Security=SSPI;"; } else { - connectionstring = "Data Source=" + server + ";Initial Catalog=" + database + ";"; + connectionString = "Data Source=" + server + ";Initial Catalog=" + database + ";"; if (integratedsecurity == "display: none;") { - connectionstring += "Integrated Security=SSPI;"; + connectionString += "Integrated Security=SSPI;"; } else { - connectionstring += "User ID=" + username + ";Password=" + password; + connectionString += "User ID=" + username + ";Password=" + password; } } - Installation installation = await InstallationService.Install(connectionstring); + + var config = new InstallConfig + { + IsMaster = false, + ConnectionString = connectionString, + }; + + Installation installation = await InstallationService.Install(config); if (installation.Success) { - Tenant tenant = new Tenant(); - tenant.Name = name; - tenant.DBConnectionString = connectionstring; - tenant.DBSchema = schema; - tenant.IsInitialized = false; + //TODO : Move to Database Manager + Tenant tenant = new Tenant + { + Name = name, + DBConnectionString = connectionString, + IsInitialized = false + }; await TenantService.AddTenantAsync(tenant); await logger.LogInformation("Tenant Created {Tenant}", tenant); diff --git a/Oqtane.Client/Modules/Admin/Tenants/Edit.razor b/Oqtane.Client/Modules/Admin/Tenants/Edit.razor index 0224cd1e..afba20e7 100644 --- a/Oqtane.Client/Modules/Admin/Tenants/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Tenants/Edit.razor @@ -20,14 +20,7 @@ - - - - - - - - + Cancel @@ -38,7 +31,6 @@ int tenantid; string name = ""; string connectionstring = ""; - string schema = ""; protected override async Task OnInitializedAsync() { @@ -50,7 +42,6 @@ { name = tenant.Name; connectionstring = tenant.DBConnectionString; - schema = tenant.DBSchema; } } catch (Exception ex) @@ -70,7 +61,6 @@ { tenant.Name = name; tenant.DBConnectionString = connectionstring; - tenant.DBSchema = schema; await TenantService.UpdateTenantAsync(tenant); await logger.LogInformation("Tenant Saved {TenantId}", tenantid); diff --git a/Oqtane.Client/Services/InstallationService.cs b/Oqtane.Client/Services/InstallationService.cs index 31a1e3dd..0a11650b 100644 --- a/Oqtane.Client/Services/InstallationService.cs +++ b/Oqtane.Client/Services/InstallationService.cs @@ -19,24 +19,21 @@ namespace Oqtane.Services _navigationManager = navigationManager; } - private string Apiurl - { - get { return CreateApiUrl(_siteState.Alias, _navigationManager.Uri, "Installation"); } - } + private string ApiUrl => CreateApiUrl(_siteState.Alias, _navigationManager.Uri, "Installation"); public async Task IsInstalled() { - return await _http.GetJsonAsync(Apiurl + "/installed"); + return await _http.GetJsonAsync(ApiUrl + "/installed"); } - public async Task Install(string connectionstring) + public async Task Install(InstallConfig config) { - return await _http.PostJsonAsync(Apiurl, connectionstring); + return await _http.PostJsonAsync(ApiUrl, config); } public async Task Upgrade() { - return await _http.GetJsonAsync(Apiurl + "/upgrade"); + return await _http.GetJsonAsync(ApiUrl + "/upgrade"); } } } diff --git a/Oqtane.Client/Services/Interfaces/IInstallationService.cs b/Oqtane.Client/Services/Interfaces/IInstallationService.cs index 580fd225..c9eeaca8 100644 --- a/Oqtane.Client/Services/Interfaces/IInstallationService.cs +++ b/Oqtane.Client/Services/Interfaces/IInstallationService.cs @@ -1,12 +1,13 @@ using Oqtane.Models; using System.Threading.Tasks; +using Oqtane.Shared; namespace Oqtane.Services { public interface IInstallationService { Task IsInstalled(); - Task Install(string connectionstring); + Task Install(InstallConfig config); Task Upgrade(); } } diff --git a/Oqtane.Client/UI/Installer.razor b/Oqtane.Client/UI/Installer.razor index 071a97bb..f2985b1c 100644 --- a/Oqtane.Client/UI/Installer.razor +++ b/Oqtane.Client/UI/Installer.razor @@ -7,117 +7,117 @@
- +
-
+
-

Database Configuration


+

Database Configuration


- - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + +
- - - -
- - - -
- - - -
- - - -
- - - -
- - - -
+ + + +
+ + + +
+ + + +
+ + + +
+ + + +
+ + + +
-

Application Administrator


+

Application Administrator


- - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + +
- - - -
- - - -
- - - -
- - - -
+ + + +
+ + + +
+ + + +
+ + + +
-
+
-

- @((MarkupString)_message) +

+ @((MarkupString) _message)
@@ -140,7 +140,7 @@ private void SetIntegratedSecurity(ChangeEventArgs e) { - if (Convert.ToBoolean((string)e.Value)) + if (Convert.ToBoolean((string) e.Value)) { _integratedSecurityDisplay = "display: none;"; } @@ -172,10 +172,20 @@ else { connectionstring += "User ID=" + _username + ";Password=" + _password; - } } - Installation installation = await InstallationService.Install(connectionstring); + + var config = new InstallConfig + { + ConnectionString = connectionstring, + HostUser = _hostUsername, + HostEmail = _hostEmail, + Password = _hostPassword, + IsMaster = true, + }; + + Installation installation = await InstallationService.Install(config); + //TODO: Should be moved to Database manager if (installation.Success) { Site site = new Site(); @@ -208,4 +218,5 @@ _message = "
Please Enter All Fields And Ensure Passwords Match And Are Greater Than 5 Characters In Length
"; } } + } diff --git a/Oqtane.Server/Controllers/InstallationController.cs b/Oqtane.Server/Controllers/InstallationController.cs index 763a570d..bb99f23f 100644 --- a/Oqtane.Server/Controllers/InstallationController.cs +++ b/Oqtane.Server/Controllers/InstallationController.cs @@ -1,16 +1,9 @@ -using DbUp; -using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Oqtane.Models; using Oqtane.Shared; -using System; -using System.Data.SqlClient; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Threading; +using Oqtane.Infrastructure; using Oqtane.Infrastructure.Interfaces; // ReSharper disable StringIndexOfIsCultureSpecific.1 @@ -22,155 +15,45 @@ namespace Oqtane.Controllers { private readonly IConfigurationRoot _config; private readonly IInstallationManager _installationManager; + private readonly DatabaseManager _databaseManager; - public InstallationController(IConfigurationRoot config, IInstallationManager installationManager) + public InstallationController(IConfigurationRoot config, IInstallationManager installationManager, DatabaseManager databaseManager) { _config = config; _installationManager = installationManager; + _databaseManager = databaseManager; } // POST api/ [HttpPost] - public Installation Post([FromBody] string connectionString) + public Installation Post([FromBody] InstallConfig config) { - var installation = new Installation { Success = false, Message = "" }; + //TODO Security ???? + var installation = new Installation {Success = false, Message = ""}; - if (ModelState.IsValid) + if (ModelState.IsValid && (!_databaseManager.IsInstalled || !config.IsMaster)) { - bool master = false; - string defaultconnectionstring = _config.GetConnectionString("DefaultConnection"); - if (string.IsNullOrEmpty(defaultconnectionstring) || connectionString == defaultconnectionstring) - { - master = true; - } + bool master = config.IsMaster; - bool exists = false; - if (master) - { - exists = IsInstalled().Success; - } + config.Alias = config.Alias ?? HttpContext.Request.Host.Value; + var result = DatabaseManager.InstallDatabase(config); - if (!exists) + if (result.Success) { - string datadirectory = AppDomain.CurrentDomain.GetData("DataDirectory").ToString(); - connectionString = connectionString.Replace("|DataDirectory|", datadirectory); - - SqlConnection connection = new SqlConnection(connectionString); - try + if (master) { - using (connection) - { - connection.Open(); - } - exists = true; - } - catch - { - // database does not exist + _config.Reload(); } - // try to create database if it does not exist - if (!exists) - { - string masterConnectionString = ""; - string databaseName = ""; - string[] fragments = connectionString.Split(';', StringSplitOptions.RemoveEmptyEntries); - foreach (string fragment in fragments) - { - if (fragment.ToLower().Contains("initial catalog=") || fragment.ToLower().Contains("database=")) - { - databaseName = fragment.Substring(fragment.IndexOf("=") + 1); - } - else - { - if (!fragment.ToLower().Contains("attachdbfilename=")) - { - masterConnectionString += fragment + ";"; - } - } - } - connection = new SqlConnection(masterConnectionString); - try - { - using (connection) - { - connection.Open(); - SqlCommand command; - if (connectionString.ToLower().Contains("attachdbfilename=")) // LocalDB - { - command = new SqlCommand("CREATE DATABASE [" + databaseName + "] ON ( NAME = '" + databaseName + "', FILENAME = '" + datadirectory + "\\" + databaseName + ".mdf')", connection); - } - else - { - command = new SqlCommand("CREATE DATABASE [" + databaseName + "]", connection); - } - command.ExecuteNonQuery(); - exists = true; - } - } - catch (Exception ex) - { - installation.Message = "Can Not Create Database - " + ex.Message; - } - - // sleep to allow SQL server to attach new database - Thread.Sleep(5000); - } - - if (exists) - { - // get master initialization script and update connectionstring and alias in seed data - string initializationScript = ""; - if (master) - { - using (StreamReader reader = new StreamReader(Directory.GetCurrentDirectory() + "\\Scripts\\Master.sql")) - { - initializationScript = reader.ReadToEnd(); - } - initializationScript = initializationScript.Replace("{ConnectionString}", connectionString.Replace(datadirectory, "|DataDirectory|")); - initializationScript = initializationScript.Replace("{Alias}", HttpContext.Request.Host.Value); - } - - var dbUpgradeConfig = DeployChanges.To.SqlDatabase(connectionString) - .WithScript(new DbUp.Engine.SqlScript("Master.sql", initializationScript)) - .WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly()); // tenant scripts should be added to /Scripts folder as Embedded Resources - var dbUpgrade = dbUpgradeConfig.Build(); - if (dbUpgrade.IsUpgradeRequired()) - { - var result = dbUpgrade.PerformUpgrade(); - if (!result.Successful) - { - installation.Message = result.Error.Message; - } - else - { - // update appsettings - if (master) - { - string config = ""; - using (StreamReader reader = new StreamReader(Directory.GetCurrentDirectory() + "\\appsettings.json")) - { - config = reader.ReadToEnd(); - } - connectionString = connectionString.Replace(datadirectory, "|DataDirectory|"); - connectionString = connectionString.Replace(@"\", @"\\"); - config = config.Replace("DefaultConnection\": \"", "DefaultConnection\": \"" + connectionString); - using (StreamWriter writer = new StreamWriter(Directory.GetCurrentDirectory() + "\\appsettings.json")) - { - writer.WriteLine(config); - } - _config.Reload(); - } - installation.Success = true; - } - } - } - } - else - { - installation.Message = "Application Is Already Installed"; + installation.Success = true; + return installation; } + + installation.Message = result.Message; + return installation; } + + installation.Message = "Application Is Already Installed"; return installation; } @@ -178,133 +61,21 @@ namespace Oqtane.Controllers [HttpGet("installed")] public Installation IsInstalled() { - var installation = new Installation { Success = false, Message = "" }; + var installation = new Installation {Success = false, Message = ""}; - string datadirectory = AppDomain.CurrentDomain.GetData("DataDirectory").ToString(); - string connectionString = _config.GetConnectionString("DefaultConnection"); - connectionString = connectionString.Replace("|DataDirectory|", datadirectory); - - if (!string.IsNullOrEmpty(connectionString)) - { - SqlConnection connection = new SqlConnection(connectionString); - try - { - using (connection) - { - connection.Open(); - } - installation.Success = true; - } - catch - { - // database does not exist - installation.Message = "Database Does Not Exist"; - } - } - else - { - installation.Message = "Connection String Has Not Been Specified In Oqtane.Server\\appsettings.json"; - } - - if (installation.Success) - { - var dbUpgradeConfig = DeployChanges.To.SqlDatabase(connectionString) - .WithScript(new DbUp.Engine.SqlScript("Master.sql", "")); - var dbUpgrade = dbUpgradeConfig.Build(); - installation.Success = !dbUpgrade.IsUpgradeRequired(); - if (!installation.Success) - { - installation.Message = "Master Installation Scripts Have Not Been Executed"; - } - else - { - using (var db = new InstallationContext(connectionString)) - { - ApplicationVersion version = db.ApplicationVersion.ToList().LastOrDefault(); - if (version == null || version.Version != Constants.Version) - { - version = new ApplicationVersion(); - version.Version = Constants.Version; - version.CreatedOn = DateTime.UtcNow; - db.ApplicationVersion.Add(version); - db.SaveChanges(); - } - } - - Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies() - .Where(item => item.FullName.Contains(".Module.")).ToArray(); - - // get tenants - using (var db = new InstallationContext(connectionString)) - { - foreach (Tenant tenant in db.Tenant.ToList()) - { - connectionString = tenant.DBConnectionString; - connectionString = connectionString.Replace("|DataDirectory|", datadirectory); - - // upgrade framework - dbUpgradeConfig = DeployChanges.To.SqlDatabase(connectionString) - .WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly()); - dbUpgrade = dbUpgradeConfig.Build(); - if (dbUpgrade.IsUpgradeRequired()) - { - var result = dbUpgrade.PerformUpgrade(); - if (!result.Successful) - { - // TODO: log result.Error.Message - problem is logger is not available here - } - } - // iterate through Oqtane module assemblies and execute any database scripts - foreach (Assembly assembly in assemblies) - { - InstallModule(assembly, connectionString); - } - } - } - } - } + installation.Success = _databaseManager.IsInstalled; + installation.Message = _databaseManager.Message; return installation; } - private void InstallModule(Assembly assembly, string connectionstring) - { - var dbUpgradeConfig = DeployChanges.To.SqlDatabase(connectionstring) - .WithScriptsEmbeddedInAssembly(assembly); // scripts must be included as Embedded Resources - var dbUpgrade = dbUpgradeConfig.Build(); - if (dbUpgrade.IsUpgradeRequired()) - { - var result = dbUpgrade.PerformUpgrade(); - if (!result.Successful) - { - // TODO: log result.Error.Message - problem is logger is not available here - } - } - } - [HttpGet("upgrade")] [Authorize(Roles = Constants.HostRole)] public Installation Upgrade() { - var installation = new Installation { Success = true, Message = "" }; + var installation = new Installation {Success = true, Message = ""}; _installationManager.UpgradeFramework(); return installation; } } - - public class InstallationContext : DbContext - { - private readonly string _connectionString; - - public InstallationContext(string connectionString) - { - _connectionString = connectionString; - } - - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - => optionsBuilder.UseSqlServer(_connectionString); - - public virtual DbSet ApplicationVersion { get; set; } - public virtual DbSet Tenant { get; set; } - } } diff --git a/Oqtane.Server/Controllers/UserController.cs b/Oqtane.Server/Controllers/UserController.cs index 19d0aaf9..d2bc94db 100644 --- a/Oqtane.Server/Controllers/UserController.cs +++ b/Oqtane.Server/Controllers/UserController.cs @@ -75,95 +75,107 @@ namespace Oqtane.Controllers [HttpPost] public async Task Post([FromBody] User user) { - User newUser = null; - if (ModelState.IsValid) { - // users created by non-administrators must be verified - bool verified = !(!User.IsInRole(Constants.AdminRole) && user.Username != Constants.HostUser); + var newUser = await CreateUser(user); + return newUser; + } - IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username); - if (identityuser == null) + return null; + } + + //TODO shoud be moved to another layer + private async Task CreateUser(User user) + { + User newUser = null; + // users created by non-administrators must be verified + bool verified = !(!User.IsInRole(Constants.AdminRole) && user.Username != Constants.HostUser); + + IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username); + if (identityuser == null) + { + identityuser = new IdentityUser(); + identityuser.UserName = user.Username; + identityuser.Email = user.Email; + identityuser.EmailConfirmed = verified; + var result = await _identityUserManager.CreateAsync(identityuser, user.Password); + if (result.Succeeded) { - identityuser = new IdentityUser(); - identityuser.UserName = user.Username; - identityuser.Email = user.Email; - identityuser.EmailConfirmed = verified; - var result = await _identityUserManager.CreateAsync(identityuser, user.Password); - if (result.Succeeded) + user.LastLoginOn = null; + user.LastIPAddress = ""; + newUser = _users.AddUser(user); + if (!verified) { - user.LastLoginOn = null; - user.LastIPAddress = ""; - newUser = _users.AddUser(user); - if (!verified) - { - Notification notification = new Notification(); - notification.SiteId = user.SiteId; - notification.FromUserId = null; - notification.ToUserId = newUser.UserId; - notification.ToEmail = ""; - notification.Subject = "User Account Verification"; - string token = await _identityUserManager.GenerateEmailConfirmationTokenAsync(identityuser); - string url = HttpContext.Request.Scheme + "://" + _tenants.GetAlias().Name + "/login?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token); - notification.Body = "Dear " + user.DisplayName + ",\n\nIn Order To Complete The Registration Of Your User Account Please Click The Link Displayed Below:\n\n" + url + "\n\nThank You!"; - notification.ParentId = null; - notification.CreatedOn = DateTime.UtcNow; - notification.IsDelivered = false; - notification.DeliveredOn = null; - _notifications.AddNotification(notification); - } - - // assign to host role if this is the host user ( initial installation ) - if (user.Username == Constants.HostUser) - { - int hostroleid = _roles.GetRoles(user.SiteId, true).Where(item => item.Name == Constants.HostRole).FirstOrDefault().RoleId; - UserRole userrole = new UserRole(); - userrole.UserId = newUser.UserId; - userrole.RoleId = hostroleid; - userrole.EffectiveDate = null; - userrole.ExpiryDate = null; - _userRoles.AddUserRole(userrole); - } - - // add folder for user - Folder folder = _folders.GetFolder(user.SiteId, "Users\\"); - if (folder != null) - { - _folders.AddFolder(new Folder { SiteId = folder.SiteId, ParentId = folder.FolderId, Name = "My Folder", Path = folder.Path + newUser.UserId.ToString() + "\\", Order = 1, IsSystem = true, - Permissions = "[{\"PermissionName\":\"Browse\",\"Permissions\":\"[" + newUser.UserId.ToString() + "]\"},{\"PermissionName\":\"View\",\"Permissions\":\"All Users\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"[" + newUser.UserId.ToString() + "]\"}]" }); - } + Notification notification = new Notification(); + notification.SiteId = user.SiteId; + notification.FromUserId = null; + notification.ToUserId = newUser.UserId; + notification.ToEmail = ""; + notification.Subject = "User Account Verification"; + string token = await _identityUserManager.GenerateEmailConfirmationTokenAsync(identityuser); + string url = HttpContext.Request.Scheme + "://" + _tenants.GetAlias().Name + "/login?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token); + notification.Body = "Dear " + user.DisplayName + ",\n\nIn Order To Complete The Registration Of Your User Account Please Click The Link Displayed Below:\n\n" + url + "\n\nThank You!"; + notification.ParentId = null; + notification.CreatedOn = DateTime.UtcNow; + notification.IsDelivered = false; + notification.DeliveredOn = null; + _notifications.AddNotification(notification); } - } - else - { - var result = await _identitySignInManager.CheckPasswordSignInAsync(identityuser, user.Password, false); - if (result.Succeeded) - { - newUser = _users.GetUser(user.Username); - } - } - if (newUser != null && user.Username != Constants.HostUser) - { - // add auto assigned roles to user for site - List roles = _roles.GetRoles(user.SiteId).Where(item => item.IsAutoAssigned).ToList(); - foreach (Role role in roles) + // assign to host role if this is the host user ( initial installation ) + if (user.Username == Constants.HostUser) { + int hostroleid = _roles.GetRoles(user.SiteId, true).Where(item => item.Name == Constants.HostRole).FirstOrDefault().RoleId; UserRole userrole = new UserRole(); userrole.UserId = newUser.UserId; - userrole.RoleId = role.RoleId; + userrole.RoleId = hostroleid; userrole.EffectiveDate = null; userrole.ExpiryDate = null; _userRoles.AddUserRole(userrole); } - } - if (newUser != null) - { - newUser.Password = ""; // remove sensitive information - _logger.Log(user.SiteId, LogLevel.Information, this, LogFunction.Create, "User Added {User}", newUser); + // add folder for user + Folder folder = _folders.GetFolder(user.SiteId, "Users\\"); + if (folder != null) + { + _folders.AddFolder(new Folder + { + SiteId = folder.SiteId, ParentId = folder.FolderId, Name = "My Folder", Path = folder.Path + newUser.UserId.ToString() + "\\", Order = 1, IsSystem = true, + Permissions = "[{\"PermissionName\":\"Browse\",\"Permissions\":\"[" + newUser.UserId.ToString() + "]\"},{\"PermissionName\":\"View\",\"Permissions\":\"All Users\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"[" + + newUser.UserId.ToString() + "]\"}]" + }); + } } } + else + { + var result = await _identitySignInManager.CheckPasswordSignInAsync(identityuser, user.Password, false); + if (result.Succeeded) + { + newUser = _users.GetUser(user.Username); + } + } + + if (newUser != null && user.Username != Constants.HostUser) + { + // add auto assigned roles to user for site + List roles = _roles.GetRoles(user.SiteId).Where(item => item.IsAutoAssigned).ToList(); + foreach (Role role in roles) + { + UserRole userrole = new UserRole(); + userrole.UserId = newUser.UserId; + userrole.RoleId = role.RoleId; + userrole.EffectiveDate = null; + userrole.ExpiryDate = null; + _userRoles.AddUserRole(userrole); + } + } + + if (newUser != null) + { + newUser.Password = ""; // remove sensitive information + _logger.Log(user.SiteId, LogLevel.Information, this, LogFunction.Create, "User Added {User}", newUser); + } return newUser; } diff --git a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs index e2bf622e..aef47901 100644 --- a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs +++ b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs @@ -115,7 +115,7 @@ namespace Microsoft.Extensions.DependencyInjection foreach (var file in assembliesFolder.EnumerateFiles($"*.{pattern}.*.dll")) { // check if assembly is already loaded - var assembly = Assemblies.FirstOrDefault(a => a.Location == file.FullName); + var assembly = Assemblies.FirstOrDefault(a =>!a.IsDynamic && a.Location == file.FullName); if (assembly == null) { // load assembly from stream to prevent locking file ( as long as dependencies are in /bin they will load as well ) diff --git a/Oqtane.Server/Infrastructure/DatabaseManager.cs b/Oqtane.Server/Infrastructure/DatabaseManager.cs new file mode 100644 index 00000000..bccbf5b8 --- /dev/null +++ b/Oqtane.Server/Infrastructure/DatabaseManager.cs @@ -0,0 +1,367 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using System.Reflection; +using DbUp; +using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Newtonsoft.Json; +using Oqtane.Controllers; +using Oqtane.Models; +using Oqtane.Repository; +using Oqtane.Shared; +using File = System.IO.File; + +namespace Oqtane.Infrastructure +{ + public class DatabaseManager + { + private readonly IConfigurationRoot _config; + private readonly IServiceScopeFactory _serviceScopeFactory; + private bool _isInstalled; + + public DatabaseManager(IConfigurationRoot config, IServiceScopeFactory serviceScopeFactory) + { + _config = config; + _serviceScopeFactory = serviceScopeFactory; + } + + public string Message { get; set; } + + public bool IsInstalled + { + get + { + if (!_isInstalled) _isInstalled = CheckInstallState(); + + return _isInstalled; + } + set => _isInstalled = value; + } + + private bool CheckInstallState() + { + var defaultConnectionString = _config.GetConnectionString("DefaultConnection"); + var result = !string.IsNullOrEmpty(defaultConnectionString); + if (result) + { + using (var scope = _serviceScopeFactory.CreateScope()) + { + var dbContext = scope.ServiceProvider.GetRequiredService(); + result = dbContext.Database.CanConnect(); + } + if (result) + { + //I think this is obsolete now and not accurate, maybe check presence of some table, Version ??? + var dbUpgradeConfig = DeployChanges + .To + .SqlDatabase(defaultConnectionString) + .WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), s => s.Contains("Master")); + + result = !dbUpgradeConfig.Build().IsUpgradeRequired(); + if (!result) Message = "Master Installation Scripts Have Not Been Executed"; + } + else + { + Message = "Database is not avaiable"; + } + } + else + { + Message = "Connection string is empty"; + } + + return result; + } + + + public static string NormalizeConnectionString(string connectionString, string dataDirectory) + { + connectionString = connectionString + .Replace("|DataDirectory|", dataDirectory); + //.Replace(@"\", @"\\"); + return connectionString; + } + + public static Installation InstallDatabase([NotNull] InstallConfig installConfig) + { + var dataDirectory = AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString(); + var result = new Installation {Success = false, Message = ""}; + + var alias = installConfig.Alias; + var connectionString = NormalizeConnectionString(installConfig.ConnectionString, dataDirectory); + + if (string.IsNullOrEmpty(connectionString) || string.IsNullOrEmpty(alias)) + { + result = new Installation + { + Success = false, + Message = "Connection string is empty", + }; + return result; + } + + result = MasterMigration(connectionString, alias, result, installConfig.IsMaster); + if (installConfig.IsMaster && result.Success) + { + WriteVersionInfo(connectionString); + TenantMigration(connectionString, dataDirectory); + UpdateOqtaneSettings(connectionString); + AddOrUpdateAppSetting("Oqtane:DefaultAlias", alias); + } + + return result; + } + + private static Installation MasterMigration(string connectionString, string alias, Installation result, bool master) + { + if (result == null) result = new Installation {Success = false, Message = string.Empty}; + + try + { + // create empty database if does not exists + // dbup database creation does not work correctly on localdb databases + using (var dbc = new DbContext(new DbContextOptionsBuilder().UseSqlServer(connectionString).Options)) + { + dbc.Database.EnsureCreated(); + } + } + catch (Exception e) + { + result = new Installation + { + Success = false, + Message = e.Message, + }; + Console.WriteLine(e); + return result; + } + + var dbUpgradeConfig = DeployChanges + .To + .SqlDatabase(connectionString) + .WithVariable("ConnectionString", connectionString) + .WithVariable("Alias", alias) + .WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), s => s.Contains("Master") && master || s.Contains("Tenant")) + ; + + var dbUpgrade = dbUpgradeConfig.Build(); + if (!dbUpgrade.IsUpgradeRequired()) + { + result.Success = true; + result.Message = string.Empty; + return result; + } + + var upgradeResult = dbUpgrade.PerformUpgrade(); + if (!upgradeResult.Successful) + { + Console.WriteLine(upgradeResult.Error.Message); + result.Message = upgradeResult.Error.Message; + } + else + { + result.Success = true; + } + + return result; + } + + private static void ModuleMigration(Assembly assembly, string connectionString) + { + var dbUpgradeConfig = DeployChanges.To.SqlDatabase(connectionString) + .WithScriptsEmbeddedInAssembly(assembly); // scripts must be included as Embedded Resources + var dbUpgrade = dbUpgradeConfig.Build(); + if (dbUpgrade.IsUpgradeRequired()) + { + var result = dbUpgrade.PerformUpgrade(); + if (!result.Successful) + { + // TODO: log result.Error.Message - problem is logger is not available here + } + } + } + + private static void WriteVersionInfo(string connectionString) + { + using (var db = new InstallationContext(connectionString)) + { + var version = db.ApplicationVersion.ToList().LastOrDefault(); + if (version == null || version.Version != Constants.Version) + { + version = new ApplicationVersion {Version = Constants.Version, CreatedOn = DateTime.UtcNow}; + db.ApplicationVersion.Add(version); + db.SaveChanges(); + } + } + } + + private static void TenantMigration(string connectionString, string dataDirectory) + { + var assemblies = AppDomain.CurrentDomain.GetAssemblies() + .Where(item => item.FullName != null && item.FullName.Contains(".Module.")).ToArray(); + + // get tenants + using (var db = new InstallationContext(connectionString)) + { + foreach (var tenant in db.Tenant.ToList()) + { + connectionString = NormalizeConnectionString(tenant.DBConnectionString, dataDirectory); + // upgrade framework + var dbUpgradeConfig = DeployChanges.To.SqlDatabase(connectionString) + .WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly(), s => s.Contains("Tenant")); + var dbUpgrade = dbUpgradeConfig.Build(); + if (dbUpgrade.IsUpgradeRequired()) + { + var result = dbUpgrade.PerformUpgrade(); + if (!result.Successful) + { + // TODO: log result.Error.Message - problem is logger is not available here + } + } + + // iterate through Oqtane module assemblies and execute any database scripts + foreach (var assembly in assemblies) ModuleMigration(assembly, connectionString); + } + } + } + + public static void UpdateOqtaneSettings(string connectionString) + { + AddOrUpdateAppSetting("ConnectionStrings:DefaultConnection", connectionString); + //AddOrUpdateAppSetting("Oqtane:DefaultAlias", connectionString); + } + + + public static 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 static 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 procress, 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 StartupMigration() + { + var defaultConnectionString = _config.GetConnectionString("DefaultConnection"); + var defaultAlias = _config.GetSection("Oqtane").GetValue("DefaultAlias", string.Empty); + + // if no values specified, fallback to IDE installer + if (string.IsNullOrEmpty(defaultConnectionString) || string.IsNullOrEmpty(defaultAlias)) + { + IsInstalled = false; + return; + } + + var result = MasterMigration(defaultConnectionString, defaultAlias, null, true); + IsInstalled = result.Success; + if (_isInstalled) + BuildDefaultSite(); + } + + public void BuildDefaultSite() + { + using (var scope = _serviceScopeFactory.CreateScope()) + { + //Gather required services + var siteRepository = scope.ServiceProvider.GetRequiredService(); + + // Build default site only if no site present + if (siteRepository.GetSites().Any()) return; + + var users = scope.ServiceProvider.GetRequiredService(); + var roles = scope.ServiceProvider.GetRequiredService(); + var userRoles = scope.ServiceProvider.GetRequiredService(); + var folders = scope.ServiceProvider.GetRequiredService(); + var identityUserManager = scope.ServiceProvider.GetRequiredService>(); + + var site = new Site + { + TenantId = -1, + Name = "Default Site", + LogoFileId = null, + DefaultThemeType = Constants.DefaultTheme, + DefaultLayoutType = Constants.DefaultLayout, + DefaultContainerType = Constants.DefaultContainer, + }; + site = siteRepository.AddSite(site); + + var user = new User + { + SiteId = site.SiteId, + Username = Constants.HostUser, + //TODO Decide default password or throw exception ?? + Password = _config.GetSection("Oqtane").GetValue("DefaultPassword", "oQtane123"), + Email = _config.GetSection("Oqtane").GetValue("DefaultEmail", "nobody@cortonso.com"), + DisplayName = Constants.HostUser, + }; + CreateHostUser(folders, userRoles, roles, users, identityUserManager, user); + } + } + + + private static void CreateHostUser(IFolderRepository folderRepository, IUserRoleRepository userRoleRepository, IRoleRepository roleRepository, IUserRepository userRepository, UserManager identityUserManager, User user) + { + var identityUser = new IdentityUser {UserName = user.Username, Email = user.Email, EmailConfirmed = true}; + var result = identityUserManager.CreateAsync(identityUser, user.Password).GetAwaiter().GetResult(); + + if (result.Succeeded) + { + user.LastLoginOn = null; + user.LastIPAddress = ""; + var newUser = userRepository.AddUser(user); + + // assign to host role if this is the host user ( initial installation ) + if (user.Username == Constants.HostUser) + { + var hostRoleId = roleRepository.GetRoles(user.SiteId, true).FirstOrDefault(item => item.Name == Constants.HostRole)?.RoleId ?? 0; + var userRole = new UserRole {UserId = newUser.UserId, RoleId = hostRoleId, EffectiveDate = null, ExpiryDate = null}; + userRoleRepository.AddUserRole(userRole); + } + + // add folder for user + var folder = folderRepository.GetFolder(user.SiteId, "Users\\"); + if (folder != null) + folderRepository.AddFolder(new Folder + { + SiteId = folder.SiteId, ParentId = folder.FolderId, Name = "My Folder", Path = folder.Path + newUser.UserId + "\\", Order = 1, IsSystem = true, + Permissions = "[{\"PermissionName\":\"Browse\",\"Permissions\":\"[" + newUser.UserId + "]\"},{\"PermissionName\":\"View\",\"Permissions\":\"All Users\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"[" + + newUser.UserId + "]\"}]", + }); + } + } + } +} diff --git a/Oqtane.Server/Oqtane.Server.csproj b/Oqtane.Server/Oqtane.Server.csproj index 32847187..7471c460 100644 --- a/Oqtane.Server/Oqtane.Server.csproj +++ b/Oqtane.Server/Oqtane.Server.csproj @@ -27,14 +27,10 @@ - - - - - - - - + + + + diff --git a/Oqtane.Server/Program.cs b/Oqtane.Server/Program.cs index b440e758..6402cf5e 100644 --- a/Oqtane.Server/Program.cs +++ b/Oqtane.Server/Program.cs @@ -4,6 +4,8 @@ using Microsoft.Extensions.Hosting; using Microsoft.AspNetCore.Blazor.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.AspNetCore; +using Microsoft.Extensions.DependencyInjection; +using Oqtane.Infrastructure; namespace Oqtane.Server { @@ -12,7 +14,14 @@ namespace Oqtane.Server #if DEBUG || RELEASE public static void Main(string[] args) { - CreateHostBuilder(args).Build().Run(); + var host = CreateHostBuilder(args).Build(); + using (var serviceScope = host.Services.GetRequiredService().CreateScope()) + { + var manager = serviceScope.ServiceProvider.GetService(); + manager.StartupMigration(); + } + //DatabaseManager.StartupMigration(); + host.Run(); } public static IHostBuilder CreateHostBuilder(string[] args) => diff --git a/Oqtane.Server/Repository/Context/DBContextBase.cs b/Oqtane.Server/Repository/Context/DBContextBase.cs index e6aceed9..247006bd 100644 --- a/Oqtane.Server/Repository/Context/DBContextBase.cs +++ b/Oqtane.Server/Repository/Context/DBContextBase.cs @@ -22,21 +22,11 @@ namespace Oqtane.Repository protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer(_tenant.DBConnectionString - .Replace("|DataDirectory|", AppDomain.CurrentDomain.GetData("DataDirectory").ToString()) + .Replace("|DataDirectory|", AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString()) ); base.OnConfiguring(optionsBuilder); } - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - base.OnModelCreating(modelBuilder); - - if (_tenant.DBSchema != "") - { - modelBuilder.HasDefaultSchema(_tenant.DBSchema); - } - } - public override int SaveChanges() { ChangeTracker.DetectChanges(); diff --git a/Oqtane.Server/Repository/Context/InstallationContext.cs b/Oqtane.Server/Repository/Context/InstallationContext.cs new file mode 100644 index 00000000..7b971a16 --- /dev/null +++ b/Oqtane.Server/Repository/Context/InstallationContext.cs @@ -0,0 +1,23 @@ +using System.Diagnostics.CodeAnalysis; +using Microsoft.EntityFrameworkCore; +using Oqtane.Models; + +namespace Oqtane.Repository +{ + + public class InstallationContext : DbContext + { + private readonly string _connectionString; + + public InstallationContext(string connectionString) + { + _connectionString = connectionString; + } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + => optionsBuilder.UseSqlServer(_connectionString); + + public virtual DbSet ApplicationVersion { get; set; } + public virtual DbSet Tenant { get; set; } + } +} diff --git a/Oqtane.Server/Repository/SiteTemplateRepository.cs b/Oqtane.Server/Repository/SiteTemplateRepository.cs index 4d3b5f7b..c88934fd 100644 --- a/Oqtane.Server/Repository/SiteTemplateRepository.cs +++ b/Oqtane.Server/Repository/SiteTemplateRepository.cs @@ -41,7 +41,7 @@ namespace Oqtane.Repository var siteTemplateObject = ActivatorUtilities.CreateInstance(_serviceProvider, siteTemplateType); siteTemplate = new SiteTemplate { - Name = (string)siteTemplateType.GetProperty("Name").GetValue(siteTemplateObject), + Name = (string)siteTemplateType.GetProperty("Name")?.GetValue(siteTemplateObject), TypeName = siteTemplateType.AssemblyQualifiedName }; siteTemplates.Add(siteTemplate); diff --git a/Oqtane.Server/Repository/TenantRepository.cs b/Oqtane.Server/Repository/TenantRepository.cs index 19acf9fa..4af682d6 100644 --- a/Oqtane.Server/Repository/TenantRepository.cs +++ b/Oqtane.Server/Repository/TenantRepository.cs @@ -49,10 +49,14 @@ namespace Oqtane.Repository } public void DeleteTenant(int tenantId) - { + { Tenant tenant = _db.Tenant.Find(tenantId); - _db.Tenant.Remove(tenant); - _db.SaveChanges(); + if (tenant != null) + { + _db.Tenant.Remove(tenant); + _db.SaveChanges(); + } + _cache.Remove("tenants"); } } diff --git a/Oqtane.Server/Repository/TenantResolver.cs b/Oqtane.Server/Repository/TenantResolver.cs index 9760c4e6..8a5b85c4 100644 --- a/Oqtane.Server/Repository/TenantResolver.cs +++ b/Oqtane.Server/Repository/TenantResolver.cs @@ -29,43 +29,41 @@ namespace Oqtane.Repository { aliasName = accessor.HttpContext.Request.Host.Value; string path = accessor.HttpContext.Request.Path.Value; - string[] segments = path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); + string[] segments = path.Split(new[] {'/'}, StringSplitOptions.RemoveEmptyEntries); if (segments.Length > 1 && segments[1] == "api" && segments[0] != "~") { aliasName += "/" + segments[0]; } + if (aliasName.EndsWith("/")) { aliasName = aliasName.Substring(0, aliasName.Length - 1); } } } - else // background processes can pass in an alias using the SiteState service + else // background processes can pass in an alias using the SiteState service { - if (siteState != null) - { - aliasId = siteState.Alias.AliasId; - } + aliasId = siteState?.Alias?.AliasId ?? -1; } // get the alias and tenant - if (aliasId != -1 || aliasName != "") + IEnumerable aliases = aliasRepository.GetAliases().ToList(); // cached + if (aliasId != -1) { - IEnumerable aliases = aliasRepository.GetAliases(); // cached - IEnumerable tenants = tenantRepository.GetTenants(); // cached + _alias = aliases.FirstOrDefault(item => item.AliasId == aliasId); + } + else + { + + _alias = aliases.FirstOrDefault(item => item.Name == aliasName + //if here is only one alias and other methods fail, take it (case of startup install) + || aliases.Count() == 1); + } - if (aliasId != -1) - { - _alias = aliases.FirstOrDefault(item => item.AliasId == aliasId); - } - else - { - _alias = aliases.FirstOrDefault(item => item.Name == aliasName); - } - if (_alias != null) - { - _tenant = tenants.FirstOrDefault(item => item.TenantId == _alias.TenantId); - } + if (_alias != null) + { + IEnumerable tenants = tenantRepository.GetTenants(); // cached + _tenant = tenants.FirstOrDefault(item => item.TenantId == _alias.TenantId); } } diff --git a/Oqtane.Server/Scripts/Master.sql b/Oqtane.Server/Scripts/Master.00.00.00.sql similarity index 96% rename from Oqtane.Server/Scripts/Master.sql rename to Oqtane.Server/Scripts/Master.00.00.00.sql index 45e474c7..e1b64042 100644 --- a/Oqtane.Server/Scripts/Master.sql +++ b/Oqtane.Server/Scripts/Master.00.00.00.sql @@ -123,7 +123,7 @@ Create seed data SET IDENTITY_INSERT [dbo].[Tenant] ON GO INSERT [dbo].[Tenant] ([TenantId], [Name], [DBConnectionString], [DBSchema], [IsInitialized], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn]) -VALUES (1, N'Master', N'{ConnectionString}', N'', 1, '', getdate(), '', getdate()) +VALUES (1, N'Master', N'$ConnectionString$', N'', 1, '', getdate(), '', getdate()) GO SET IDENTITY_INSERT [dbo].[Tenant] OFF GO @@ -131,7 +131,7 @@ GO SET IDENTITY_INSERT [dbo].[Alias] ON GO INSERT [dbo].[Alias] ([AliasId], [Name], [TenantId], [SiteId], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn]) -VALUES (1, N'{Alias}', 1, 1, '', getdate(), '', getdate()) +VALUES (1, N'$Alias$', 1, 1, '', getdate(), '', getdate()) GO SET IDENTITY_INSERT [dbo].[Alias] OFF GO diff --git a/Oqtane.Server/Scripts/Master.00.00.01.sql b/Oqtane.Server/Scripts/Master.00.00.01.sql new file mode 100644 index 00000000..02943e96 --- /dev/null +++ b/Oqtane.Server/Scripts/Master.00.00.01.sql @@ -0,0 +1,2 @@ +alter table Tenant drop column DBSchema +go diff --git a/Oqtane.Server/Scripts/00.00.00.sql b/Oqtane.Server/Scripts/Tenant.00.00.00.sql similarity index 96% rename from Oqtane.Server/Scripts/00.00.00.sql rename to Oqtane.Server/Scripts/Tenant.00.00.00.sql index 666fe978..5157b401 100644 --- a/Oqtane.Server/Scripts/00.00.00.sql +++ b/Oqtane.Server/Scripts/Tenant.00.00.00.sql @@ -404,7 +404,7 @@ Create indexes */ -CREATE UNIQUE NONCLUSTERED INDEX IX_Setting ON dbo.Setting +CREATE UNIQUE NONCLUSTERED INDEX IX_Setting ON [dbo].Setting ( EntityName, EntityId, @@ -412,13 +412,13 @@ CREATE UNIQUE NONCLUSTERED INDEX IX_Setting ON dbo.Setting ) ON [PRIMARY] GO -CREATE UNIQUE NONCLUSTERED INDEX IX_User ON dbo.[User] +CREATE UNIQUE NONCLUSTERED INDEX IX_User ON [dbo].[User] ( Username ) ON [PRIMARY] GO -CREATE UNIQUE NONCLUSTERED INDEX IX_Permission ON dbo.Permission +CREATE UNIQUE NONCLUSTERED INDEX IX_Permission ON [dbo].Permission ( SiteId, EntityName, @@ -429,7 +429,7 @@ CREATE UNIQUE NONCLUSTERED INDEX IX_Permission ON dbo.Permission ) ON [PRIMARY] GO -CREATE UNIQUE NONCLUSTERED INDEX IX_Page ON dbo.Page +CREATE UNIQUE NONCLUSTERED INDEX IX_Page ON [dbo].Page ( SiteId, [Path], @@ -437,14 +437,14 @@ CREATE UNIQUE NONCLUSTERED INDEX IX_Page ON dbo.Page ) ON [PRIMARY] GO -CREATE UNIQUE NONCLUSTERED INDEX IX_UserRole ON dbo.UserRole +CREATE UNIQUE NONCLUSTERED INDEX IX_UserRole ON [dbo].UserRole ( RoleId, UserId ) ON [PRIMARY] GO -CREATE UNIQUE NONCLUSTERED INDEX IX_Folder ON dbo.Folder +CREATE UNIQUE NONCLUSTERED INDEX IX_Folder ON [dbo].Folder ( SiteId, [Path] diff --git a/Oqtane.Server/Scripts/00.00.01.sql b/Oqtane.Server/Scripts/Tenant.00.00.01.sql similarity index 100% rename from Oqtane.Server/Scripts/00.00.01.sql rename to Oqtane.Server/Scripts/Tenant.00.00.01.sql diff --git a/Oqtane.Server/SilentInstall.json b/Oqtane.Server/SilentInstall.json new file mode 100644 index 00000000..cf3e04b4 --- /dev/null +++ b/Oqtane.Server/SilentInstall.json @@ -0,0 +1,7 @@ +{ + "Alias" : "", + "DefaultConnection" : "", + "HostUser" : "host", + "Password" : "", + "HostEmail" : "" +} diff --git a/Oqtane.Server/Startup.cs b/Oqtane.Server/Startup.cs index 07a47ee7..190e4adb 100644 --- a/Oqtane.Server/Startup.cs +++ b/Oqtane.Server/Startup.cs @@ -19,9 +19,12 @@ using Oqtane.Infrastructure.Interfaces; using Oqtane.Repository; using Oqtane.Security; using Oqtane.Services; -// DO NOT REMOVE - needed for client-side Blazor using Oqtane.Shared; + +#if WASM +// DO NOT REMOVE - needed for client-side Blazor using Microsoft.AspNetCore.ResponseCompression; +#endif namespace Oqtane { @@ -113,7 +116,7 @@ namespace Oqtane services.AddDbContext(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection") - .Replace("|DataDirectory|", AppDomain.CurrentDomain.GetData("DataDirectory").ToString()) + .Replace("|DataDirectory|", AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString()) )); services.AddDbContext(options => { }); @@ -160,7 +163,8 @@ namespace Oqtane services.AddSingleton(Configuration); services.AddSingleton(); services.AddSingleton(); - + services.AddSingleton(); + // register transient scoped core services services.AddTransient(); services.AddTransient(); @@ -203,6 +207,8 @@ namespace Oqtane { c.SwaggerDoc("v1", new OpenApiInfo { Title = "Oqtane", Version = "v1" }); }); + + } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. diff --git a/Oqtane.Server/appsettings.json b/Oqtane.Server/appsettings.json index 7f07c90a..9e226a4d 100644 --- a/Oqtane.Server/appsettings.json +++ b/Oqtane.Server/appsettings.json @@ -1,5 +1,9 @@ { "ConnectionStrings": { "DefaultConnection": "" + }, + "Oqtane": { + "DefaultAlias": "", + "DefaultPassword": "" } } diff --git a/Oqtane.Shared/Models/Tenant.cs b/Oqtane.Shared/Models/Tenant.cs index bf6c4627..3f0a518a 100644 --- a/Oqtane.Shared/Models/Tenant.cs +++ b/Oqtane.Shared/Models/Tenant.cs @@ -7,9 +7,7 @@ namespace Oqtane.Models public int TenantId { get; set; } public string Name { get; set; } public string DBConnectionString { get; set; } - public string DBSchema { get; set; } public bool IsInitialized { get; set; } - public string CreatedBy { get; set; } public DateTime CreatedOn { get; set; } public string ModifiedBy { get; set; } diff --git a/Oqtane.Shared/Shared/InstallConfig.cs b/Oqtane.Shared/Shared/InstallConfig.cs new file mode 100644 index 00000000..f7f065b4 --- /dev/null +++ b/Oqtane.Shared/Shared/InstallConfig.cs @@ -0,0 +1,12 @@ +namespace Oqtane.Shared +{ + public class InstallConfig + { + public string Alias { get; set; } + public string ConnectionString { get; set; } + public string HostUser { get; set; } + public string Password { get; set; } + public string HostEmail { get; set; } + public bool IsMaster { get; set; } + } +}