diff --git a/GR.sln.DotSettings b/GR.sln.DotSettings index bf28d37b..0c0994ed 100644 --- a/GR.sln.DotSettings +++ b/GR.sln.DotSettings @@ -24,6 +24,7 @@ True True True + True True True True diff --git a/src/GR.Extensions/GR.Application.Extension/GR.WebApplication/Extensions/ServiceCollectionsExtensions.cs b/src/GR.Extensions/GR.Application.Extension/GR.WebApplication/Extensions/ServiceCollectionsExtensions.cs index 83cf6b35..ba182b57 100644 --- a/src/GR.Extensions/GR.Application.Extension/GR.WebApplication/Extensions/ServiceCollectionsExtensions.cs +++ b/src/GR.Extensions/GR.Application.Extension/GR.WebApplication/Extensions/ServiceCollectionsExtensions.cs @@ -14,11 +14,9 @@ using GR.Core.Helpers.ModelBinders.ModelBinderProviders; using GR.Core.Razor.Extensions; using GR.Localization.Abstractions.Extensions; -using GR.Localization.Abstractions.Models; using GR.Localization.Abstractions.Models.Config; using GR.Notifications.Abstractions.Extensions; using GR.Notifications.Hub.Hubs; -using GR.PageRender.Abstractions.Extensions; using GR.WebApplication.Helpers; using GR.WebApplication.Helpers.AppConfigurations; using Microsoft.AspNetCore.Builder; @@ -180,9 +178,6 @@ public static IGearAppBuilder UseGearWebApp(this IApplicationBuilder app, Action app.UseExceptionHandler("/Home/Error"); } - //-----------------------Custom url redirection Usage------------------------------------- - if (configuration.UseCustomUrlRewrite) app.UseUrlRewriteModule(); - //----------------------------------Origin Cors Usage------------------------------------- if (configuration.UseDefaultCorsConfiguration) app.UseConfiguredCors(); diff --git a/src/GR.Extensions/GR.Application.Extension/GR.WebApplication/Helpers/AppConfigurations/GearAppBuilderConfig.cs b/src/GR.Extensions/GR.Application.Extension/GR.WebApplication/Helpers/AppConfigurations/GearAppBuilderConfig.cs index fedcbdeb..acb17317 100644 --- a/src/GR.Extensions/GR.Application.Extension/GR.WebApplication/Helpers/AppConfigurations/GearAppBuilderConfig.cs +++ b/src/GR.Extensions/GR.Application.Extension/GR.WebApplication/Helpers/AppConfigurations/GearAppBuilderConfig.cs @@ -23,11 +23,6 @@ public class GearAppBuilderConfig /// public IConfiguration Configuration { get; set; } - /// - /// Use dynamic pages url rewrite - /// - public virtual bool UseCustomUrlRewrite { get; set; } = true; - /// /// Use default cors /// diff --git a/src/GR.Extensions/GR.Application.Extension/GR.WebApplication/Services/ExportDataIO.cs b/src/GR.Extensions/GR.Application.Extension/GR.WebApplication/Services/ExportDataIO.cs deleted file mode 100644 index b6898b2a..00000000 --- a/src/GR.Extensions/GR.Application.Extension/GR.WebApplication/Services/ExportDataIO.cs +++ /dev/null @@ -1,85 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.IO.Compression; -using System.Linq; -using System.Threading.Tasks; -using Newtonsoft.Json; - -namespace GR.WebApplication.Services -{ - public static class ExportDataIo - { - /// - /// - /// - /// - /// - public static MemoryStream CreateZipArchive(IDictionary files) - { - var zipStream = new MemoryStream(); - using (var zip = new ZipArchive(zipStream, ZipArchiveMode.Create, true)) - { - foreach (var o in files) - { - try - { - var pageEntry = zip.CreateEntry(o.Key); - - using (var entryStream = pageEntry.Open()) - { - o.Value.CopyTo(entryStream); - } - } - catch (Exception e) - { - Console.WriteLine(e); - } - } - } - zipStream.Position = 0; - return zipStream; - } - - /// - /// Decompress - /// - /// - /// - public static void Decompress(MemoryStream stream, Action action) - { - var zip = new ZipArchive(stream, ZipArchiveMode.Read, true); - action.Invoke(zip); - } - - /// - /// GetConnectionString data from zip archive - /// - /// - /// - /// - /// - public static async Task GetDataFromZipArchiveEntry(this IEnumerable entries, string file) - { - var entities = entries.FirstOrDefault(x => x.Name.Equals(file)); - if (entities == null) return default; - - var entStream = entities.Open(); - using (var strReader = new StreamReader(entStream)) - { - var data = await strReader.ReadToEndAsync(); - if (string.IsNullOrEmpty(data)) return default; - try - { - return JsonConvert.DeserializeObject(data); - } - catch (Exception e) - { - Console.WriteLine(e); - } - } - - return default; - } - } -} diff --git a/src/GR.Extensions/GR.Application.Extension/GR.WebApplication/Services/ExportManager.cs b/src/GR.Extensions/GR.Application.Extension/GR.WebApplication/Services/ExportManager.cs deleted file mode 100644 index 91d2e5dc..00000000 --- a/src/GR.Extensions/GR.Application.Extension/GR.WebApplication/Services/ExportManager.cs +++ /dev/null @@ -1,196 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using GR.Core.Helpers; -using GR.Core.Helpers.ConnectionStrings; -using GR.DynamicEntityStorage.Abstractions; -using GR.DynamicEntityStorage.Abstractions.Extensions; -using GR.Entities.Abstractions; -using GR.Entities.Abstractions.Models.Tables; -using GR.Entities.Abstractions.ViewModels.Table; -using GR.Entities.Data; -using GR.Forms.Abstractions; -using GR.Identity.Data; -using GR.PageRender.Abstractions; -using Microsoft.EntityFrameworkCore; -using Newtonsoft.Json; - -namespace GR.WebApplication.Services -{ - public static class ExportManager - { - /// - /// CreateZipArchive data async - /// - /// - public static async Task<(MemoryStream, string, string)> ExportAsync() - { - var entitiesDbContext = IoC.Resolve(); - var dynamicService = IoC.Resolve(); - var applicationDbContext = IoC.Resolve(); - var formContext = IoC.Resolve(); - var pageContext = IoC.Resolve(); - var dynamicEntities = entitiesDbContext.Table - .Where(x => !x.IsPartOfDbContext) - .Include(x => x.TableFields); - - //var entityFrameWorkEntities = entitiesDbContext.Table - // .Where(x => x.IsPartOfDbContext) - // .Include(x => x.TableFields); - - var dynamicData = new Dictionary>(); - var frameworkData = new Dictionary>(); - - //Extract values from from dynamic entities - foreach (var entity in dynamicEntities) - { - var req = await dynamicService.Table(entity.Name).GetAllWithInclude(); - if (req.IsSuccess) - { - dynamicData.Add(entity.Name, req.Result); - } - } - - //Extract values from Entity FrameWork DbSet declarations - //foreach (var entity in entityFrameWorkEntities) - //{ - // var req = await dynamicService.Table(entity.Name).GetAll(); - // if (req.IsSuccess) - // { - // frameworkData.Add(entity.Name, req.Result); - // } - //} - - var zipStream = ExportDataIo.CreateZipArchive(new Dictionary - { - { - "forms.json", new MemoryStream(Encoding.ASCII.GetBytes(Serialize(formContext.Forms - .Include(x => x.Columns) - .Include(x => x.Rows) - .Include(x => x.Fields) - .Include(x => x.Stages) - .Include(x => x.Settings).ToList()))) - }, - { - "pages.json", new MemoryStream(Encoding.ASCII.GetBytes(Serialize(pageContext.Pages - .Include(x => x.PageScripts) - .Include(x => x.PageStyles) - .Include(x => x.PageType) - .Include(x => x.Settings).ToArray()))) - }, - { - "dynamicEntities.json", new MemoryStream(Encoding.ASCII.GetBytes(Serialize(dynamicEntities))) - }, - { - "templates.json", new MemoryStream(Encoding.ASCII.GetBytes(Serialize(pageContext.Templates.ToList()))) - }, - { - "roles.json", new MemoryStream(Encoding.ASCII.GetBytes(Serialize(applicationDbContext.Roles.ToList()))) - }, - { - "tenants.json", new MemoryStream(Encoding.ASCII.GetBytes(Serialize(applicationDbContext.Tenants.ToList()))) - }, - { - "users.json", new MemoryStream(Encoding.ASCII.GetBytes(Serialize(applicationDbContext.Users.ToList()))) - }, - { - "userRoles.json", new MemoryStream(Encoding.ASCII.GetBytes(Serialize(applicationDbContext.UserRoles.ToList()))) - }, - { - "rolePermissions.json", new MemoryStream(Encoding.ASCII.GetBytes(Serialize(applicationDbContext.RolePermissions.ToList()))) - }, - { - "blocksCategories.json", new MemoryStream(Encoding.ASCII.GetBytes(Serialize(pageContext.BlockCategories.ToList()))) - }, - { - "blocks.json", new MemoryStream(Encoding.ASCII.GetBytes(Serialize(pageContext.Blocks.ToList()))) - }, - { - "dynamicEntitiesData.json", new MemoryStream(Encoding.ASCII.GetBytes(Serialize(dynamicData))) - }, - { - "frameworkEntitiesData.json", new MemoryStream(Encoding.ASCII.GetBytes(Serialize(frameworkData))) - } - }); - var date = DateTime.Now; - return (zipStream, "application/octet-stream", $"export_system_{date.Minute}_{date.Hour}_{date.Day}_{date.Month}_{date.Year}.zip"); - } - - /// - /// Import async - /// - /// - /// - public static ResultModel Import(MemoryStream memStream) - { - var result = new ResultModel(); - - ExportDataIo.Decompress(memStream, async zip => - { - var context = IoC.Resolve(); - var tableService = IoC.Resolve(); - var dynamicContext = IoC.Resolve(); - var dynamicValues = await zip.Entries.GetDataFromZipArchiveEntry>>("dynamicEntitiesData.json"); - if (dynamicValues == null) return; - - //Import dynamic entities - var dynamicEntities = await zip.Entries.GetDataFromZipArchiveEntry>("dynamicEntities.json"); - if (dynamicEntities == null) return; - foreach (var entity in dynamicEntities) - { - if (await context.Table.AnyAsync(x => x.Name == entity.Name && x.TenantId == entity.TenantId)) - continue; - await context.Table.AddAsync(entity); - tableService.CreateSqlTable(entity, context.GetConnectionString()); - foreach (var tableField in entity.TableFields) - { - tableService.AddFieldSql(new CreateTableFieldViewModel - { - Name = tableField.Name, - DisplayName = tableField.DisplayName, - AllowNull = tableField.AllowNull, - Id = tableField.Id, - TableId = entity.Id, - DataType = tableField.DataType, - Description = tableField.Description - }, entity.Name, context.GetConnectionString(), true, entity.EntityType); - } - if (dynamicValues.ContainsKey(entity.Name)) - { - await dynamicContext.Table(entity.Name).AddRange(dynamicValues[entity.Name]); - } - } - await context.SaveChangesAsync(); - }); - - result.IsSuccess = true; - return result; - } - - /// - /// Serialize - /// - /// - /// - /// - private static string Serialize(TObject obj) - { - try - { - return JsonConvert.SerializeObject(obj, Formatting.Indented, new JsonSerializerSettings - { - ReferenceLoopHandling = ReferenceLoopHandling.Ignore - }); - } - catch (Exception e) - { - Console.WriteLine(e); - } - - return string.Empty; - } - } -} diff --git a/src/GR.Extensions/GR.Backup.Extension/GR.Backup.Abstractions/BackgroundServices/BackupTimeService.cs b/src/GR.Extensions/GR.Backup.Extension/GR.Backup.Abstractions/BackgroundServices/BackupTimeService.cs index 5fc304b7..3cfacc27 100755 --- a/src/GR.Extensions/GR.Backup.Extension/GR.Backup.Abstractions/BackgroundServices/BackupTimeService.cs +++ b/src/GR.Extensions/GR.Backup.Extension/GR.Backup.Abstractions/BackgroundServices/BackupTimeService.cs @@ -1,5 +1,4 @@ using System; -using System.IO; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Hosting; @@ -30,9 +29,9 @@ namespace GR.Backup.Abstractions.BackgroundServices /// /// Inject backup service /// - private readonly IBackupService _backupService; + private readonly IBackupService _backupService; - public BackupTimeService(ILogger> logger, IOptions options, IBackupService backupService) + public BackupTimeService(ILogger> logger, IOptions options, IBackupService backupService) { _logger = logger; _backupService = backupService; diff --git a/src/GR.Extensions/GR.Backup.Extension/GR.Backup.Abstractions/Extensions/ServiceCollectionExtensions.cs b/src/GR.Extensions/GR.Backup.Extension/GR.Backup.Abstractions/Extensions/ServiceCollectionExtensions.cs index b4e0c68f..4ec86864 100755 --- a/src/GR.Extensions/GR.Backup.Extension/GR.Backup.Abstractions/Extensions/ServiceCollectionExtensions.cs +++ b/src/GR.Extensions/GR.Backup.Extension/GR.Backup.Abstractions/Extensions/ServiceCollectionExtensions.cs @@ -2,6 +2,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using GR.Backup.Abstractions.Models; +using GR.Core.Extensions; namespace GR.Backup.Abstractions.Extensions { @@ -13,15 +14,26 @@ public static class ServiceCollectionExtensions /// /// /// - public static IServiceCollection RegisterDatabaseBackupRunnerModule(this IServiceCollection services, IConfiguration configuration) - where TRunner : class, IHostedService + public static IServiceCollection RegisterDatabaseBackupRunnerModule(this IServiceCollection services, IConfiguration configuration) where TBackupSettings : BackupSettings - where TBackupService : class, IBackupService + where TBackupService : class, IBackupService { - services.AddTransient, TBackupService>(); + services.AddGearSingleton(); + services.Configure(configuration.GetSection(nameof(BackupSettings))); + return services; + } - //Run background service for backup database + /// + /// Register auto backup runner + /// + /// + /// + /// + public static IServiceCollection RegisterDatabaseBackgroundService(this IServiceCollection services) + where TRunner : class, IHostedService + { + //Register background service for backup database services.AddHostedService(); return services; } diff --git a/src/GR.Extensions/GR.Backup.Extension/GR.Backup.Abstractions/IBackupService.cs b/src/GR.Extensions/GR.Backup.Extension/GR.Backup.Abstractions/IBackupService.cs index f854e75f..382496a8 100755 --- a/src/GR.Extensions/GR.Backup.Extension/GR.Backup.Abstractions/IBackupService.cs +++ b/src/GR.Extensions/GR.Backup.Extension/GR.Backup.Abstractions/IBackupService.cs @@ -1,9 +1,10 @@ using System.Collections.Generic; -using GR.Backup.Abstractions.Models; +using GR.Backup.Abstractions.ViewModels; +using GR.Core.Helpers; namespace GR.Backup.Abstractions { - public interface IBackupService where TSettings : BackupSettings + public interface IBackupService { /// /// Make backup @@ -20,6 +21,19 @@ public interface IBackupService where TSettings : BackupSettings /// List of backups /// /// - IEnumerable GetBackups(); + IEnumerable GetBackups(); + + /// + /// Download backup + /// + /// + /// + DownloadBackupResultModel DownloadBackup(string backupName); + + /// + /// Clear backups + /// + /// + ResultModel Clear(); } } \ No newline at end of file diff --git a/src/GR.Extensions/GR.Backup.Extension/GR.Backup.Abstractions/ViewModels/BackupViewModel.cs b/src/GR.Extensions/GR.Backup.Extension/GR.Backup.Abstractions/ViewModels/BackupViewModel.cs new file mode 100644 index 00000000..8ace1f6b --- /dev/null +++ b/src/GR.Extensions/GR.Backup.Extension/GR.Backup.Abstractions/ViewModels/BackupViewModel.cs @@ -0,0 +1,32 @@ +using System; + +namespace GR.Backup.Abstractions.ViewModels +{ + public sealed class BackupViewModel + { + /// + /// File name + /// + public string Name { get; set; } + + /// + /// Physic file path + /// + public string Path { get; set; } + + /// + /// File size + /// + public long Size { get; set; } + + /// + /// Creation date + /// + public DateTime CreationDate { get; set; } + + /// + /// Backup extension + /// + public string Extension { get; set; } + } +} \ No newline at end of file diff --git a/src/GR.Extensions/GR.Backup.Extension/GR.Backup.Abstractions/ViewModels/DownloadBackupResultModel.cs b/src/GR.Extensions/GR.Backup.Extension/GR.Backup.Abstractions/ViewModels/DownloadBackupResultModel.cs new file mode 100644 index 00000000..fbf060d7 --- /dev/null +++ b/src/GR.Extensions/GR.Backup.Extension/GR.Backup.Abstractions/ViewModels/DownloadBackupResultModel.cs @@ -0,0 +1,10 @@ +using GR.Core.Helpers; + +namespace GR.Backup.Abstractions.ViewModels +{ + public sealed class DownloadBackupResultModel : ResultModel + { + public string Name { get; set; } + public string FullPath { get; set; } + } +} diff --git a/src/GR.Extensions/GR.Backup.Extension/GR.Backup.PostgresSql/PostGreBackupService.cs b/src/GR.Extensions/GR.Backup.Extension/GR.Backup.PostgresSql/PostGreBackupService.cs index 15d339e0..7652615c 100755 --- a/src/GR.Extensions/GR.Backup.Extension/GR.Backup.PostgresSql/PostGreBackupService.cs +++ b/src/GR.Extensions/GR.Backup.Extension/GR.Backup.PostgresSql/PostGreBackupService.cs @@ -9,12 +9,14 @@ using System.Linq; using System.Runtime.InteropServices; using System.Text; +using GR.Backup.Abstractions.ViewModels; +using GR.Core.Helpers; namespace GR.Backup.PostGresSql { //Special folders https://developers.redhat.com/blog/2018/11/07/dotnet-special-folder-api-linux/ [Author(Authors.LUPEI_NICOLAE)] - public class PostGreBackupService : IBackupService + public class PostGreBackupService : IBackupService { #region Injectable @@ -46,14 +48,117 @@ public virtual void Backup() else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { //TODO: Backup on mac os env + ExecuteLinuxDump(); + } + } + + /// + /// Get provider name + /// + /// + public virtual string GetProviderName() => GetType().Name; + + /// + /// Get backups + /// + /// + public virtual IEnumerable GetBackups() + { + var files = Directory.GetFiles(GetDirectoryPath()) + .ToList(); + + foreach (var file in files) + { + var fileInfo = new FileInfo(file); + yield return new BackupViewModel + { + Name = fileInfo.Name, + Path = file, + CreationDate = fileInfo.CreationTime, + Size = fileInfo.Length, + Extension = fileInfo.Extension + }; + } + } + + /// + /// Clear all + /// + public virtual ResultModel Clear() + { + var response = new ResultModel(); + var backups = GetBackups(); + foreach (var backup in backups) + { + try + { + File.Delete(backup.Path); + } + catch (Exception e) + { + Console.WriteLine(e); + response.Errors.Add(new ErrorModel(e.HelpLink, e.Message)); + } + } + + response.IsSuccess = !response.Errors.Any(); + return response; + } + + /// + /// Download file + /// + /// + /// + public virtual DownloadBackupResultModel DownloadBackup(string backupName) + { + var path = Path.Combine(GetDirectoryPath(), backupName); + var result = new DownloadBackupResultModel + { + FullPath = path, + Name = backupName + }; + + if (!File.Exists(path)) + { + result.Errors.Add(new ErrorModel + { + Key = string.Empty, + Message = "File not found" + }); + return result; + } + + var blob = File.ReadAllBytes(path); + result.Result = blob; + result.IsSuccess = true; + return result; + } + + #region Helpers + + /// + /// Get directory path + /// + /// + protected virtual string GetDirectoryPath() + { + var userProfilePath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); + var directoryPath = Path.Combine(userProfilePath, $"backup\\{_options.Value.BackupFolder}"); + if (!Directory.Exists(directoryPath)) + { + Directory.CreateDirectory(directoryPath); } + + return directoryPath; } + /// /// Build dump output file /// /// - protected string BuildDumpOutputFile() + protected virtual string BuildDumpOutputFile() { var directoryPath = GetDirectoryPath(); var currentDate = DateTime.Now; @@ -65,7 +170,7 @@ protected string BuildDumpOutputFile() /// /// Execute dump in windows /// - protected void ExecuteWindowsDump() + protected virtual void ExecuteWindowsDump() { var outputFile = BuildDumpOutputFile(); var dumpCommand = "\"" + _options.Value.PgDumpPath + "\"" + " -Fc" + " -h " + _options.Value.Host + " -p " + @@ -126,7 +231,7 @@ protected void ExecuteWindowsDump() /// /// Execute linux dump file backup /// - protected void ExecuteLinuxDump() + protected virtual void ExecuteLinuxDump() { var outputFile = BuildDumpOutputFile(); var command = $"PGPASSWORD=\"{_options.Value.Password}\" pg_dump -h {_options.Value.Host} -p {_options.Value.Port} -U {_options.Value.User} -F c -b -v -f \"{outputFile}\" {_options.Value.Database}"; @@ -149,36 +254,6 @@ protected void ExecuteLinuxDump() Debug.WriteLine(result); } - /// - /// Get provider name - /// - /// - public virtual string GetProviderName() => GetType().Name; - - /// - /// Get backups - /// - /// - public virtual IEnumerable GetBackups() - { - var files = Directory.GetFiles(GetDirectoryPath()).ToList(); - return files; - } - - /// - /// Get directory path - /// - /// - protected virtual string GetDirectoryPath() - { - var userProfilePath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); - var directoryPath = Path.Combine(userProfilePath, $"backup\\{_options.Value.BackupFolder}"); - if (!Directory.Exists(directoryPath)) - { - Directory.CreateDirectory(directoryPath); - } - - return directoryPath; - } + #endregion } } \ No newline at end of file diff --git a/src/GR.Extensions/GR.Backup.Extension/GR.Backup.PostgresSql/PostGreSqlBackupSettings.cs b/src/GR.Extensions/GR.Backup.Extension/GR.Backup.PostgresSql/PostGreSqlBackupSettings.cs index 0e807482..86a3334c 100755 --- a/src/GR.Extensions/GR.Backup.Extension/GR.Backup.PostgresSql/PostGreSqlBackupSettings.cs +++ b/src/GR.Extensions/GR.Backup.Extension/GR.Backup.PostgresSql/PostGreSqlBackupSettings.cs @@ -37,6 +37,6 @@ public sealed class PostGreSqlBackupSettings : BackupSettings /// /// File extension /// - public string FileExtension { get; set; } = "pgbackup"; + public string FileExtension { get; set; } = "backup"; } } diff --git a/src/GR.Extensions/GR.Backup.Extension/GR.Backup.Razor/Controllers/DataBaseBackupController.cs b/src/GR.Extensions/GR.Backup.Extension/GR.Backup.Razor/Controllers/DataBaseBackupController.cs index 38e588e1..ba8ec0cc 100755 --- a/src/GR.Extensions/GR.Backup.Extension/GR.Backup.Razor/Controllers/DataBaseBackupController.cs +++ b/src/GR.Extensions/GR.Backup.Extension/GR.Backup.Razor/Controllers/DataBaseBackupController.cs @@ -1,19 +1,63 @@ -using Microsoft.AspNetCore.Authorization; +using System.Linq; +using GR.Backup.Abstractions; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using GR.Core; +using GR.Core.Razor.BaseControllers; namespace GR.Backup.Razor.Controllers { [Authorize(Roles = GlobalResources.Roles.ADMINISTRATOR)] - public class DataBaseBackupController : Controller + public class DataBaseBackupController : BaseGearController { + #region Injectable + + /// + /// Inject backup + /// + private readonly IBackupService _backupService; + + #endregion + + public DataBaseBackupController(IBackupService backupService) + { + _backupService = backupService; + } + /// /// Backup manager /// /// + [HttpGet] public IActionResult Index() { - return View(); + var backups = _backupService.GetBackups() + .OrderByDescending(x => x.CreationDate) + .ToList(); + return View(backups); + } + + /// + /// Download backup + /// + /// + /// + [HttpGet] + [Route(DefaultApiRouteTemplate)] + public IActionResult DownloadBackup(string backupName) + { + var blobRequest = _backupService.DownloadBackup(backupName); + if (!blobRequest.IsSuccess) return NotFound(); + return File(blobRequest.Result, "application/octet-stream", blobRequest.Name); } + + /// + /// Clear all + /// + /// + [HttpPost] + [Route(DefaultApiRouteTemplate)] + public JsonResult ClearAll() + => Json(_backupService.Clear()); } } \ No newline at end of file diff --git a/src/GR.Extensions/GR.Backup.Extension/GR.Backup.Razor/GR.Backup.Razor.csproj b/src/GR.Extensions/GR.Backup.Extension/GR.Backup.Razor/GR.Backup.Razor.csproj index fcfe5d79..3be5cc13 100755 --- a/src/GR.Extensions/GR.Backup.Extension/GR.Backup.Razor/GR.Backup.Razor.csproj +++ b/src/GR.Extensions/GR.Backup.Extension/GR.Backup.Razor/GR.Backup.Razor.csproj @@ -11,10 +11,12 @@ + + diff --git a/src/GR.Extensions/GR.Core.Extension/GR.Core.Razor/Controllers/HomeController.cs b/src/GR.Extensions/GR.Core.Extension/GR.Core.Razor/Controllers/HomeController.cs index 8fc4e3ae..e22c153b 100644 --- a/src/GR.Extensions/GR.Core.Extension/GR.Core.Razor/Controllers/HomeController.cs +++ b/src/GR.Extensions/GR.Core.Extension/GR.Core.Razor/Controllers/HomeController.cs @@ -1,6 +1,6 @@ using System.Diagnostics; using System.Linq; -using GR.Core.Helpers; +using GR.Core.Razor.Helpers; using GR.Core.Razor.ViewModels; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; diff --git a/src/GR.Extensions/GR.Core.Extension/GR.Core/Helpers/AppRoutes.cs b/src/GR.Extensions/GR.Core.Extension/GR.Core.Razor/Helpers/AppRoutes.cs similarity index 88% rename from src/GR.Extensions/GR.Core.Extension/GR.Core/Helpers/AppRoutes.cs rename to src/GR.Extensions/GR.Core.Extension/GR.Core.Razor/Helpers/AppRoutes.cs index 4870426b..53328fe3 100644 --- a/src/GR.Extensions/GR.Core.Extension/GR.Core/Helpers/AppRoutes.cs +++ b/src/GR.Extensions/GR.Core.Extension/GR.Core.Razor/Helpers/AppRoutes.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace GR.Core.Helpers +namespace GR.Core.Razor.Helpers { public static class AppRoutes { diff --git a/src/GR.Extensions/GR.Core.Extension/GR.Core/Helpers/IoC.cs b/src/GR.Extensions/GR.Core.Extension/GR.Core/Helpers/IoC.cs index a5301d0a..98dfee25 100644 --- a/src/GR.Extensions/GR.Core.Extension/GR.Core/Helpers/IoC.cs +++ b/src/GR.Extensions/GR.Core.Extension/GR.Core/Helpers/IoC.cs @@ -5,13 +5,27 @@ using Castle.Windsor.MsDependencyInjection; using GR.Core.Exceptions; using GR.Core.Extensions; +using GR.Core.Helpers.Patterns; namespace GR.Core.Helpers { public static class IoC { - private static IWindsorContainer _container; - public static IWindsorContainer Container => _container ?? (_container = new WindsorContainer()); + /// + /// IoC container + /// + public static IWindsorContainer Container => Singleton.Instance; + + ///// + ///// IoC container + ///// + //private static IWindsorContainer _container; + + ///// + ///// Container resolver + ///// + //public static IWindsorContainer Container => _container ?? (_container = new WindsorContainer()); + /// /// Register new service @@ -46,10 +60,26 @@ public static void RegisterTransientService() whe public static void RegisterTransientService(TImplementation instance) where TImplementation : class, TAbstraction where TAbstraction : class { if (!IsServiceRegistered()) - Container.Register(Component.For().Instance(instance) + Container.Register(Component.For() + .Instance(instance) .LifestyleTransient()); } + /// + /// Register named transient service + /// + /// + /// + /// + public static void RegisterTransientService(string providerName) where TImplementation : class, TAbstraction where TAbstraction : class + { + if (!IsServiceRegistered(providerName)) + Container.Register(Component.For() + .ImplementedBy() + .LifestyleTransient() + .Named(providerName)); + } + /// /// Register singleton service /// @@ -101,15 +131,36 @@ public static void RegisterScopedService() where .LifestyleCustom().ImplementedBy(typeof(TImplementation))); } + /// + /// Register service + /// + /// + /// + /// + public static void RegisterService(Func, ComponentRegistration> configuration) + where TImplementation : class, TAbstraction where TAbstraction : class + { + var component = Component.For() + .ImplementedBy(); + component = configuration(component); + Container.Register(component); + } + /// /// Check if service is registered /// /// /// public static bool IsServiceRegistered() - { - return Container.Kernel.HasComponent(typeof(TService)); - } + => Container.Kernel.HasComponent(typeof(TService)); + + /// + /// Check if service is registered + /// + /// + /// + public static bool IsServiceRegistered(string provider) + => Container.Kernel.HasComponent(provider); /// /// Check if service is registered @@ -117,9 +168,7 @@ public static bool IsServiceRegistered() /// /// private static bool IsServiceRegistered(Type service) - { - return Container.Kernel.HasComponent(service); - } + => Container.Kernel.HasComponent(service); /// /// Resolve generic type diff --git a/src/GR.Extensions/GR.Core.Extension/GR.Core/Helpers/Patterns/Singleton.cs b/src/GR.Extensions/GR.Core.Extension/GR.Core/Helpers/Patterns/Singleton.cs new file mode 100644 index 00000000..b20e1a31 --- /dev/null +++ b/src/GR.Extensions/GR.Core.Extension/GR.Core/Helpers/Patterns/Singleton.cs @@ -0,0 +1,39 @@ +using System; + +namespace GR.Core.Helpers.Patterns +{ + public static class Singleton + where TResolver : T + { + // We now have a lock object that will be used to synchronize threads + // during first access to the Singleton. + private static readonly object _lock = new object(); + + /// + /// Stored instance + /// + private static T _instance; + + /// + /// Retrieve instance + /// + public static T Instance + { + get + { + if (_instance == null) + { + lock (_lock) + { + if (_instance == null) + { + _instance = Activator.CreateInstance(); + } + } + } + + return _instance; + } + } + } +} diff --git a/src/GR.Extensions/GR.Render.Extension/GR.PageRender.Abstractions/Extensions/ServiceCollectionExtensions.cs b/src/GR.Extensions/GR.Render.Extension/GR.PageRender.Abstractions/Extensions/ServiceCollectionExtensions.cs index 68224e22..5477074f 100644 --- a/src/GR.Extensions/GR.Render.Extension/GR.PageRender.Abstractions/Extensions/ServiceCollectionExtensions.cs +++ b/src/GR.Extensions/GR.Render.Extension/GR.PageRender.Abstractions/Extensions/ServiceCollectionExtensions.cs @@ -1,5 +1,4 @@ using System; -using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using GR.Audit.Abstractions.Extensions; @@ -8,10 +7,7 @@ using GR.Core.Extensions; using GR.Core.Helpers; using GR.PageRender.Abstractions.Events; -using GR.PageRender.Abstractions.Helpers; -using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; namespace GR.PageRender.Abstractions.Extensions { @@ -63,75 +59,5 @@ public static IServiceCollection RegisterViewModelService(thi services.AddGearTransient(); return services; } - - /// - /// Use custom url rewrite module - /// - /// - /// - public static void UseUrlRewriteModule(this IApplicationBuilder app) - => app.Use(async (ctx, next) => - { - try - { - if (GearApplication.Configured) await ctx.ConfiguredSystem(next); - else await ctx.NonConfiguredSystem(next); - } - catch (Exception e) - { - Console.WriteLine(e); - } - }); - - - /// - /// On non configured system action - /// - /// - /// - public static async Task NonConfiguredSystem(this HttpContext ctx, Func next) - { - if (ctx.Request.Cookies.Count >= 2) ctx.DeleteCookies(); - if (ctx.Request.Path.Value != "/" - && UrlRewriteHelper.IsNotSystemRoute(ctx.Request.Path.Value) - && !ctx.Request.Path.Value.ToLowerInvariant().StartsWith("/installer", StringComparison.Ordinal)) - { - var originalPath = ctx.Request.Path.Value; - ctx.Items["originalPath"] = originalPath; - ctx.Request.Path = "/Installer"; - await next(); - } - else - await next(); - } - - /// - /// On configured system - /// - /// - /// - /// - public static async Task ConfiguredSystem(this HttpContext ctx, Func next) - { - try - { - UrlRewriteHelper.CheckLanguage(ref ctx); - await next(); - var isClientUrl = await UrlRewriteHelper.ParseClientRequestAsync(ctx); - if (isClientUrl) await next(); - else if (ctx.Response.StatusCode == StatusCodes.Status404NotFound && !ctx.Response.HasStarted) - { - //Re-execute the request so the user gets the error page - var originalPath = ctx.Request.Path.Value; - ctx.Items["originalPath"] = originalPath; - ctx.Request.Path = "/Handler/NotFound"; - await next(); - } - } - catch (Exception e) - { - Console.WriteLine(e); - } - } } } diff --git a/src/GR.Extensions/GR.Render.Extension/GR.PageRender.Razor/Extensions/ServiceCollectionExtensions.cs b/src/GR.Extensions/GR.Render.Extension/GR.PageRender.Razor/Extensions/ServiceCollectionExtensions.cs index b286b062..881b70bf 100755 --- a/src/GR.Extensions/GR.Render.Extension/GR.PageRender.Razor/Extensions/ServiceCollectionExtensions.cs +++ b/src/GR.Extensions/GR.Render.Extension/GR.PageRender.Razor/Extensions/ServiceCollectionExtensions.cs @@ -1,9 +1,13 @@ -using GR.Core; +using System; +using System.Threading.Tasks; +using GR.Core; using GR.Core.Extensions; using GR.PageRender.Abstractions; using GR.PageRender.Razor.Helpers; using GR.UI.Menu.Abstractions; using GR.UI.Menu.Abstractions.Events; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; namespace GR.PageRender.Razor.Extensions @@ -43,5 +47,75 @@ public static IServiceCollection AddPageAclService(this IServic return services; } + + /// + /// Use custom url rewrite module + /// + /// + /// + public static void UseUrlRewriteModule(this IApplicationBuilder app) + => app.Use(async (ctx, next) => + { + try + { + if (GearApplication.Configured) await ctx.ConfiguredSystem(next); + else await ctx.NonConfiguredSystem(next); + } + catch (Exception e) + { + Console.WriteLine(e); + } + }); + + + /// + /// On non configured system action + /// + /// + /// + public static async Task NonConfiguredSystem(this HttpContext ctx, Func next) + { + if (ctx.Request.Cookies.Count >= 2) ctx.DeleteCookies(); + if (ctx.Request.Path.Value != "/" + && UrlRewriteHelper.IsNotSystemRoute(ctx.Request.Path.Value) + && !ctx.Request.Path.Value.ToLowerInvariant().StartsWith("/installer", StringComparison.Ordinal)) + { + var originalPath = ctx.Request.Path.Value; + ctx.Items["originalPath"] = originalPath; + ctx.Request.Path = "/Installer"; + await next(); + } + else + await next(); + } + + /// + /// On configured system + /// + /// + /// + /// + public static async Task ConfiguredSystem(this HttpContext ctx, Func next) + { + try + { + UrlRewriteHelper.CheckLanguage(ref ctx); + await next(); + var isClientUrl = await UrlRewriteHelper.ParseClientRequestAsync(ctx); + if (isClientUrl) await next(); + else if (ctx.Response.StatusCode == StatusCodes.Status404NotFound && !ctx.Response.HasStarted) + { + //Re-execute the request so the user gets the error page + var originalPath = ctx.Request.Path.Value; + ctx.Items["originalPath"] = originalPath; + ctx.Request.Path = "/Handler/NotFound"; + await next(); + } + } + catch (Exception e) + { + Console.WriteLine(e); + } + } } } \ No newline at end of file diff --git a/src/GR.Extensions/GR.Render.Extension/GR.PageRender.Abstractions/Helpers/UrlRewriteHelper.cs b/src/GR.Extensions/GR.Render.Extension/GR.PageRender.Razor/Helpers/UrlRewriteHelper.cs similarity index 97% rename from src/GR.Extensions/GR.Render.Extension/GR.PageRender.Abstractions/Helpers/UrlRewriteHelper.cs rename to src/GR.Extensions/GR.Render.Extension/GR.PageRender.Razor/Helpers/UrlRewriteHelper.cs index 1e2563bd..6c9b7e5e 100644 --- a/src/GR.Extensions/GR.Render.Extension/GR.PageRender.Abstractions/Helpers/UrlRewriteHelper.cs +++ b/src/GR.Extensions/GR.Render.Extension/GR.PageRender.Razor/Helpers/UrlRewriteHelper.cs @@ -6,7 +6,8 @@ using System.Web; using GR.Core; using GR.Core.Extensions; -using GR.Core.Helpers; +using GR.Core.Razor.Helpers; +using GR.PageRender.Abstractions; using GR.PageRender.Abstractions.Constants; using GR.PageRender.Abstractions.Models.Pages; using Microsoft.AspNetCore.Http; @@ -15,7 +16,7 @@ #pragma warning disable 1998 -namespace GR.PageRender.Abstractions.Helpers +namespace GR.PageRender.Razor.Helpers { public static class UrlRewriteHelper { diff --git a/src/GR.WebHosts/GR.Cms/Controllers/ExportController.cs b/src/GR.WebHosts/GR.Cms/Controllers/ExportController.cs deleted file mode 100755 index b8b2a868..00000000 --- a/src/GR.WebHosts/GR.Cms/Controllers/ExportController.cs +++ /dev/null @@ -1,62 +0,0 @@ -using GR.Core; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using System.IO; -using System.Threading.Tasks; -using GR.WebApplication.Services; - -namespace GR.Cms.Controllers -{ - [Authorize(Roles = GlobalResources.Roles.ADMINISTRATOR)] - public class DataController : Controller - { - /// - /// CreateZipArchive data - /// - /// - public async Task Export() - { - var (stream, contentType, name) = await ExportManager.ExportAsync(); - return File(stream, contentType, name); - } - - /// - /// Import config zip file - /// - /// - [HttpGet] - public IActionResult Import() - { - return View(); - } - - /// - /// Apply zip config on current system - /// - /// - /// - public IActionResult Import(IFormFile file) - { - if (file == null) - { - ModelState.AddModelError(string.Empty, "File not selected!"); - return View(); - } - - if (file.ContentType != "application/x-zip-compressed") - { - ModelState.AddModelError(string.Empty, "File need to be in zip format!"); - return View(); - } - - var memStream = new MemoryStream(); - file.CopyTo(memStream); - var response = ExportManager.Import(memStream); - if (response.IsSuccess) - return RedirectToAction("Index", "Home"); - ModelState.AddModelError(string.Empty, "Fail to import data"); - return View(); - } - } -} \ No newline at end of file diff --git a/src/GR.WebHosts/GR.Cms/Startup.cs b/src/GR.WebHosts/GR.Cms/Startup.cs index a8a4d061..47e09a09 100644 --- a/src/GR.WebHosts/GR.Cms/Startup.cs +++ b/src/GR.WebHosts/GR.Cms/Startup.cs @@ -156,13 +156,18 @@ public Startup(IConfiguration configuration, IHostingEnvironment env) : base(con /// /// public override void Configure(IApplicationBuilder app) - => app.UseGearWebApp(config => + { + app.UseGearWebApp(config => { config.AppName = "Web APP"; config.HostingEnvironment = HostingEnvironment; config.Configuration = Configuration; }); + //-----------------------Page Module Custom url redirection------------------------------------- + app.UseUrlRewriteModule(); + } + /// /// This method gets called by the runtime. Use this method to add services to the container. /// @@ -263,10 +268,9 @@ public override IServiceProvider ConfigureServices(IServiceCollection services) options.EnableSensitiveDataLogging(); }); - //------------------------------Database backup Module------------------------------------- - config.GearServices.RegisterDatabaseBackupRunnerModule, - PostGreSqlBackupSettings, PostGreBackupService>(Configuration); + config.GearServices.RegisterDatabaseBackupRunnerModule(Configuration) + .RegisterDatabaseBackgroundService>(); //------------------------------------Processes Module------------------------------------- config.GearServices.AddProcessesModule() diff --git a/src/GR.WebHosts/GR.Cms/Views/DataBaseBackup/Index.cshtml b/src/GR.WebHosts/GR.Cms/Views/DataBaseBackup/Index.cshtml new file mode 100644 index 00000000..95e4a404 --- /dev/null +++ b/src/GR.WebHosts/GR.Cms/Views/DataBaseBackup/Index.cshtml @@ -0,0 +1,75 @@ +@using ByteSizeLib + +@model IEnumerable + +@{ + ViewData["Title"] = "Database Backups"; +} + +
+
+ Clear +
+
+ + + + + + + + + + + + @foreach (var item in Model) + { + + + + + + + + } + + @if (!Model.Any()) + { + + + + } + +
NameExtensionCreation dateSizeActions
@item.Name@item.Extension@item.CreationDate@ByteSize.FromBytes(item.Size).ToString() + Download +
No backups!
+
+
+ + +@section Scripts{ + +} \ No newline at end of file diff --git a/src/GR.WebHosts/GR.Cms/appsettings.Development.json b/src/GR.WebHosts/GR.Cms/appsettings.Development.json index 07672bb6..8ea25206 100644 --- a/src/GR.WebHosts/GR.Cms/appsettings.Development.json +++ b/src/GR.WebHosts/GR.Cms/appsettings.Development.json @@ -121,9 +121,16 @@ } }, "BackupSettings": { - "Enabled": true, + "Enabled": false, "BackupFolder": "App_Name", - "Interval": 24 + "Interval": "24", + "PgDumpPath": "C:\\Program Files\\PostgreSQL\\11\\bin\\pg_dump.exe", + "Host": "localhost", + "Port": "5432", + "User": "postgres", + "Password": "1111", + "Database": "DbName", + "FileExtension": "backup" }, "EmailSettings": { "Enabled": false, diff --git a/src/GR.WebHosts/GR.Cms/appsettings.Stage.json b/src/GR.WebHosts/GR.Cms/appsettings.Stage.json index 07b37d8a..a06f3d21 100644 --- a/src/GR.WebHosts/GR.Cms/appsettings.Stage.json +++ b/src/GR.WebHosts/GR.Cms/appsettings.Stage.json @@ -121,9 +121,16 @@ } }, "BackupSettings": { - "Enabled": true, + "Enabled": false, "BackupFolder": "App_Name", - "Interval": 24 + "Interval": "24", + "PgDumpPath": "C:\\Program Files\\PostgreSQL\\11\\bin\\pg_dump.exe", + "Host": "localhost", + "Port": "5432", + "User": "postgres", + "Password": "1111", + "Database": "DbName", + "FileExtension": "backup" }, "EmailSettings": { "Enabled": false, diff --git a/src/GR.WebHosts/GR.Cms/appsettings.json b/src/GR.WebHosts/GR.Cms/appsettings.json index e8051806..9e57d0f8 100644 --- a/src/GR.WebHosts/GR.Cms/appsettings.json +++ b/src/GR.WebHosts/GR.Cms/appsettings.json @@ -121,9 +121,16 @@ } }, "BackupSettings": { - "Enabled": true, + "Enabled": false, "BackupFolder": "App_Name", - "Interval": 24 + "Interval": "24", + "PgDumpPath": "C:\\Program Files\\PostgreSQL\\11\\bin\\pg_dump.exe", + "Host": "localhost", + "Port": "5432", + "User": "postgres", + "Password": "1111", + "Database": "DbName", + "FileExtension": "backup" }, "EmailSettings": { "Enabled": false,