From 1a2342b955de534c06fe9dd005bf41b9631047f9 Mon Sep 17 00:00:00 2001 From: "akpaev.e" Date: Sun, 3 Aug 2025 23:21:11 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=BF=D0=BE=D0=B4=D0=B4=D0=B5=D1=80=D0=B6=D0=BA?= =?UTF-8?q?=D0=B0=20=D0=BE=D1=82=D0=BB=D0=B0=D0=B4=D0=BA=D0=B8=20=D1=81?= =?UTF-8?q?=D0=BA=D1=80=D0=B8=D0=BF=D1=82=D0=BE=D0=B2=20=D0=B8=20=D0=B7?= =?UTF-8?q?=D0=B0=D0=B4=D0=B0=D1=87=D0=B8=20=D0=BE=D0=B1=D1=89=D0=B5=D0=B3?= =?UTF-8?q?=D0=BE=20=D0=BD=D0=B0=D0=B7=D0=BD=D0=B0=D1=87=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- oneswiss-agent/Helpers/SqlHelper.cs | 73 ++ .../MaintenanceStepContext.cs | 2 +- .../MaintenanceTaskExecutor.cs | 280 +++++- oneswiss-agent/oneswiss-agent.csproj | 3 +- .../MaintenanceTasks/CopyInfoBaseStepDto.cs | 16 + .../DeleteExtensionStepDto.cs | 10 + .../ExecuteOneScriptStepDto.cs | 14 + .../LoadConfigurationStepDto.cs | 14 + .../MaintenanceTasks/LoadExtensionStepDto.cs | 16 + .../LockConnectionsStepDto.cs | 12 + .../MaintenanceTasks/MaintenanceStepDto.cs | 31 +- .../MaintenanceTasks/MaintenanceTaskDto.cs | 8 +- .../StartExternalDataProcessorStepDto.cs | 10 + .../UpdateConfigurationStepDto.cs | 10 + .../MaintenanceTasks/MaintenanceStepKind.cs | 4 +- .../OneScriptExecutor.cs | 67 +- .../Oscript/RasServiceWrapper.cs | 2 +- .../oneswiss-oscript-integration.csproj | 4 +- oneswiss-server/AppDbContext.cs | 10 +- oneswiss-server/AutoMapper/DtoProfile.cs | 9 + .../Components/ItemDeletingDialog.razor | 2 + .../Components/Layout/NavMenu.razor | 4 +- .../Components/EditItem.razor | 260 ++++- .../MaintenanceTasks/Components/Index.razor | 36 +- .../Pages/MaintenanceTasks/Log.razor | 2 +- .../Pages/MaintenanceTasks/StepNode.cs | 59 +- .../Pages/MaintenanceTasks/StepWidget.razor | 158 ++- .../Pages/MaintenanceTasks/Templates.razor | 3 - .../Shared/DatabaseObjectsTable.razor | 8 +- .../Components/Shared/EditDatabaseForm.razor | 6 +- oneswiss-server/Extensions/GuidExtensions.cs | 7 + .../MaintenanceStepKindExtensions.cs | 1 + .../20250801085631_Initial.Designer.cs | 944 ------------------ .../20250801194707_CustomNotifications.cs | 56 -- ....cs => 20250803202021_Initial.Designer.cs} | 372 ++++++- ...1_Initial.cs => 20250803202021_Initial.cs} | 435 +++++++- .../Migrations/AppDbContextModelSnapshot.cs | 368 ++++++- oneswiss-server/Models/Agent.cs | 4 + oneswiss-server/Models/DatabaseObject.cs | 4 + .../MaintenanceTasks/CopyInfoBaseStep.cs | 22 + .../MaintenanceTasks/DeleteExtensionStep.cs | 9 + .../MaintenanceTasks/ExecuteOneScriptStep.cs | 12 + .../MaintenanceTasks/LoadConfigurationStep.cs | 15 + .../MaintenanceTasks/LoadExtensionStep.cs | 18 + .../MaintenanceTasks/LockConnectionsStep.cs | 12 + .../MaintenanceTasks/MaintenanceStep.cs | 114 +-- .../MaintenanceTasks/MaintenanceTask.cs | 3 + .../StartExternalDataProcessorStep.cs | 7 + .../UpdateConfigurationStep.cs | 11 + 49 files changed, 2213 insertions(+), 1334 deletions(-) create mode 100644 oneswiss-agent/Helpers/SqlHelper.cs create mode 100644 oneswiss-common/DTO/MaintenanceTasks/CopyInfoBaseStepDto.cs create mode 100644 oneswiss-common/DTO/MaintenanceTasks/DeleteExtensionStepDto.cs create mode 100644 oneswiss-common/DTO/MaintenanceTasks/ExecuteOneScriptStepDto.cs create mode 100644 oneswiss-common/DTO/MaintenanceTasks/LoadConfigurationStepDto.cs create mode 100644 oneswiss-common/DTO/MaintenanceTasks/LoadExtensionStepDto.cs create mode 100644 oneswiss-common/DTO/MaintenanceTasks/LockConnectionsStepDto.cs create mode 100644 oneswiss-common/DTO/MaintenanceTasks/StartExternalDataProcessorStepDto.cs create mode 100644 oneswiss-common/DTO/MaintenanceTasks/UpdateConfigurationStepDto.cs create mode 100644 oneswiss-server/Extensions/GuidExtensions.cs delete mode 100644 oneswiss-server/Migrations/20250801085631_Initial.Designer.cs delete mode 100644 oneswiss-server/Migrations/20250801194707_CustomNotifications.cs rename oneswiss-server/Migrations/{20250801194707_CustomNotifications.Designer.cs => 20250803202021_Initial.Designer.cs} (72%) rename oneswiss-server/Migrations/{20250801085631_Initial.cs => 20250803202021_Initial.cs} (64%) create mode 100644 oneswiss-server/Models/MaintenanceTasks/CopyInfoBaseStep.cs create mode 100644 oneswiss-server/Models/MaintenanceTasks/DeleteExtensionStep.cs create mode 100644 oneswiss-server/Models/MaintenanceTasks/ExecuteOneScriptStep.cs create mode 100644 oneswiss-server/Models/MaintenanceTasks/LoadConfigurationStep.cs create mode 100644 oneswiss-server/Models/MaintenanceTasks/LoadExtensionStep.cs create mode 100644 oneswiss-server/Models/MaintenanceTasks/LockConnectionsStep.cs create mode 100644 oneswiss-server/Models/MaintenanceTasks/StartExternalDataProcessorStep.cs create mode 100644 oneswiss-server/Models/MaintenanceTasks/UpdateConfigurationStep.cs diff --git a/oneswiss-agent/Helpers/SqlHelper.cs b/oneswiss-agent/Helpers/SqlHelper.cs new file mode 100644 index 0000000..a068d33 --- /dev/null +++ b/oneswiss-agent/Helpers/SqlHelper.cs @@ -0,0 +1,73 @@ +using System.Data; +using Microsoft.Data.SqlClient; +using OneSwiss.Common.DTO; +using OneSwiss.V8.Platform.RemoteAdministration; + +namespace OneSwiss.Agent.Helpers; + +public record BackupInfo( + string DatabaseName, + DateTime BackupStartDate, + DateTime BackupFinishDate, + string BackupType, + decimal BackupSizeMB, + string PhysicalDeviceName); + +public static class SqlHelper +{ + public static async Task GetLastBackupInfo( + V8InfoBaseDetails infoBaseDetails, + CredentialsDto credentials, + CancellationToken cancellationToken) + { + await using var connection = new SqlConnection(GetConnectionString(infoBaseDetails, credentials)); + await connection.OpenAsync(cancellationToken); + + const string query = + """ + SELECT TOP 1 + b.database_name AS DatabaseName, + b.backup_start_date AS BackupStartDate, + b.backup_finish_date AS BackupFinishDate, + CASE b.type + WHEN 'D' THEN 'Full' + WHEN 'I' THEN 'Differential' + WHEN 'L' THEN 'Log' + END AS BackupType, + b.backup_size / 1024 / 1024 AS BackupSizeMB, + mf.physical_device_name AS PhysicalDeviceName + FROM msdb.dbo.backupset b + INNER JOIN msdb.dbo.backupmediafamily mf ON b.media_set_id = mf.media_set_id + WHERE b.type = 'D' + AND b.database_name = @DatabaseName + ORDER BY b.backup_finish_date DESC + """; + + await using var command = new SqlCommand(query, connection); + + command.Parameters.Add(new SqlParameter("@DatabaseName", SqlDbType.NVarChar, 128) + { + Value = infoBaseDetails.DbName + }); + + await using var reader = await command.ExecuteReaderAsync( + CommandBehavior.SingleRow, + cancellationToken); + + if (await reader.ReadAsync(cancellationToken)) + { + return new BackupInfo( + DatabaseName: reader.GetString("DatabaseName"), + BackupStartDate: reader.GetDateTime("BackupStartDate"), + BackupFinishDate: reader.GetDateTime("BackupFinishDate"), + BackupType: reader.GetString("BackupType"), + BackupSizeMB: reader.GetDecimal("BackupSizeMB"), + PhysicalDeviceName: reader.GetString("PhysicalDeviceName")); + } + + return null; + } + + private static string GetConnectionString(V8InfoBaseDetails infoBaseDetails, CredentialsDto credentials) + => $"Server={infoBaseDetails.DbServer};Database={infoBaseDetails.DbName};User Id={credentials.User};Password={credentials.Password};"; +} \ No newline at end of file diff --git a/oneswiss-agent/Services/MaintenanceTasks/MaintenanceStepContext.cs b/oneswiss-agent/Services/MaintenanceTasks/MaintenanceStepContext.cs index 19e9e20..b3593ee 100644 --- a/oneswiss-agent/Services/MaintenanceTasks/MaintenanceStepContext.cs +++ b/oneswiss-agent/Services/MaintenanceTasks/MaintenanceStepContext.cs @@ -11,7 +11,7 @@ namespace OneSwiss.Agent.Services.MaintenanceTasks; public class MaintenanceStepContext { public MaintenanceTaskDto Task { get; set; } = null!; - public InfoBaseDto InfoBase { get; set; } = null!; + public InfoBaseDto? InfoBase { get; set; } = null!; public MaintenanceStepDto Step { get; set; } = null!; public string AccessCode { get; set; } = string.Empty; public Dictionary Files { get; set; } = []; diff --git a/oneswiss-agent/Services/MaintenanceTasks/MaintenanceTaskExecutor.cs b/oneswiss-agent/Services/MaintenanceTasks/MaintenanceTaskExecutor.cs index c3ea649..e41761b 100644 --- a/oneswiss-agent/Services/MaintenanceTasks/MaintenanceTaskExecutor.cs +++ b/oneswiss-agent/Services/MaintenanceTasks/MaintenanceTaskExecutor.cs @@ -1,6 +1,7 @@ using System.CommandLine.Parsing; using System.Text.RegularExpressions; using OneSwiss.Agent.Extensions; +using OneSwiss.Agent.Helpers; using OneSwiss.Agent.Oscript; using OneSwiss.Common.DTO; using OneSwiss.Common.DTO.MaintenanceTasks; @@ -56,7 +57,10 @@ public class MaintenanceTaskExecutor : BackgroundService try { - await StartMaintenanceTask(task, stoppingToken); + if (task.CommonDestination) + await StartCommonDestinationMaintenanceTask(task, stoppingToken); + else + await StartMaintenanceTask(task, stoppingToken); } catch (Exception e) { @@ -64,6 +68,87 @@ public class MaintenanceTaskExecutor : BackgroundService } } } + + private async Task StartCommonDestinationMaintenanceTask(MaintenanceTaskDto task, CancellationToken cancellationToken) + { + await SendTaskLog(task, "Начало выполнения задачи", false, false, cancellationToken); + + try + { + var v8Files = await SaveTaskV8Files(task, cancellationToken); + + var log = new List(); + + var context = new MaintenanceStepContext + { + Task = task, + Log = log, + Step = task.Steps.GetRootStep(), + Files = v8Files + }; + + try + { + while (!cancellationToken.IsCancellationRequested) + { + if (log.Count > 0) + { + await SendStepLog(log, cancellationToken); + log.Clear(); + } + + if (context.Step.NodeKind == MaintenanceStepNodeKind.TryCatch) + { + try + { + await HandleTaskStepNode(context, cancellationToken); + + if (context.Step.LeftStepId is not null) + context.Step = task.Steps.GetStep(context.Step.LeftStepId); + else + break; + } + catch (Exception e) + { + AddStepLogItem(context, e.ToString(), true); + + if (context.Step.RightStepId is not null) + context.Step = task.Steps.GetStep(context.Step.RightStepId); + else + break; + } + } + else + { + await HandleTaskStepNode(context, cancellationToken); + + if (context.Step.LeftStepId is not null) + context.Step = task.Steps.GetStep(context.Step.LeftStepId); + else + break; + } + } + + AddStepLogItem(context, "Завершено", false, true); + await SendStepLog(log, cancellationToken); + } + catch (Exception e) + { + AddStepLogItem(context, e.ToString(), true, true); + await SendStepLog(log, cancellationToken); + } + finally + { + DeleteV8Files(context.Files, true); + } + + await SendTaskLog(task, "Завершение выполнения задачи", false, true, cancellationToken); + } + catch (Exception e) + { + await SendTaskLog(task, e.ToString(), true, true, cancellationToken); + } + } private async Task StartMaintenanceTask(MaintenanceTaskDto task, CancellationToken cancellationToken) { @@ -140,7 +225,7 @@ public class MaintenanceTaskExecutor : BackgroundService { try { - await HandleTaskStepNode(context); + await HandleTaskStepNode(context, stoppingToken); if (context.Step.LeftStepId is not null) context.Step = task.Steps.GetStep(context.Step.LeftStepId); @@ -159,7 +244,7 @@ public class MaintenanceTaskExecutor : BackgroundService } else { - await HandleTaskStepNode(context); + await HandleTaskStepNode(context, stoppingToken); if (context.Step.LeftStepId is not null) context.Step = task.Steps.GetStep(context.Step.LeftStepId); @@ -224,7 +309,7 @@ public class MaintenanceTaskExecutor : BackgroundService return result; } - private async Task HandleTaskStepNode(MaintenanceStepContext context) + private async Task HandleTaskStepNode(MaintenanceStepContext context, CancellationToken cancellationToken) { AddStepLogItem(context, $"Обработка шага \"{context.Step.Kind.GetDisplay()}\""); @@ -257,6 +342,9 @@ public class MaintenanceTaskExecutor : BackgroundService case MaintenanceStepKind.ExecuteOneScript: ExecuteOneScript(context); break; + case MaintenanceStepKind.CopyInfoBase: + await CopyInfoBase(context, cancellationToken); + break; default: throw new Exception($"Неизвестный тип шага \"{context.Step.Kind.GetDisplay()}\""); } @@ -271,7 +359,7 @@ public class MaintenanceTaskExecutor : BackgroundService IsError = isError, IsFinish = isFinish, TimeStamp = DateTime.Now, - InfoBaseId = context.InfoBase.Id, + InfoBaseId = context.InfoBase?.Id, StepId = context.Step.Id, TaskId = context.Task.Id }); @@ -300,17 +388,27 @@ public class MaintenanceTaskExecutor : BackgroundService await using var scope = _serviceProvider.CreateAsyncScope(); using var downloader = scope.ServiceProvider.GetRequiredService(); - var filesToDownload = task.Steps - .Where(c => c.File is not null) - .Select(c => c.File!) - .DistinctBy(c => c.Id) - .ToList(); + var filesToDownload = new List(); + + foreach (var maintenanceStepDto in task.Steps) + { + if (maintenanceStepDto.Kind == MaintenanceStepKind.ExecuteOneScript && !maintenanceStepDto.ExecuteOneScriptStep!.DebugMode) + filesToDownload.Add(maintenanceStepDto.ExecuteOneScriptStep!.File); + else if (maintenanceStepDto.Kind == MaintenanceStepKind.StartExternalDataProcessor) + filesToDownload.Add(maintenanceStepDto.StartExternalDataProcessorStep!.File); + else if (maintenanceStepDto.Kind == MaintenanceStepKind.UpdateConfiguration) + filesToDownload.Add(maintenanceStepDto.UpdateConfigurationStep!.File); + else if (maintenanceStepDto.Kind == MaintenanceStepKind.LoadConfiguration && !maintenanceStepDto.LoadExtensionStep!.FromConfigRepository) + filesToDownload.Add(maintenanceStepDto.LoadConfigurationStep!.File!); + else if (maintenanceStepDto.Kind == MaintenanceStepKind.LoadExtension && !maintenanceStepDto.LoadExtensionStep!.FromConfigRepository) + filesToDownload.Add(maintenanceStepDto.LoadExtensionStep!.File!); + } if (filesToDownload.Count == 0) return []; await SendTaskLog(task, "Загрузка файлов для выполнения шагов", false, false, cancellationToken); - var result = await downloader.Download(_serverConnection, filesToDownload, cancellationToken); + var result = await downloader.Download(_serverConnection, filesToDownload.DistinctBy(c => c.Id).ToList(), cancellationToken); await SendTaskLog(task, "Загрузка файлов для выполнения шагов завершена", false, false, cancellationToken); return result; @@ -319,18 +417,41 @@ public class MaintenanceTaskExecutor : BackgroundService private async Task DumpConfigRepositories(Dictionary files, MaintenanceTaskDto task, CancellationToken cancellationToken) { - var fromRepsSteps = task.Steps.Where(c => c.FromConfigRepository).ToList(); + var fromRepsSteps = task.Steps.Where(c => + { + switch (c.Kind) + { + case MaintenanceStepKind.LoadConfiguration when c.LoadConfigurationStep!.FromConfigRepository: + case MaintenanceStepKind.LoadExtension when c.LoadExtensionStep!.FromConfigRepository: + return true; + default: + return false; + } + }) + .Select(c => + { + switch (c.Kind) + { + case MaintenanceStepKind.LoadConfiguration when c.LoadConfigurationStep!.FromConfigRepository: + return (Step: c, c.LoadConfigurationStep!.ConfigurationRepository); + case MaintenanceStepKind.LoadExtension when c.LoadExtensionStep!.FromConfigRepository: + return (Step: c, c.LoadExtensionStep!.ConfigurationRepository); + default: + throw new NotImplementedException(); + } + }).ToList(); + if (fromRepsSteps.Count == 0) return; await SendTaskLog(task, "Выгрузка конфигураций из хранилищ", false, false, cancellationToken); - Parallel.ForEach(task.Steps.Where(c => c.FromConfigRepository), step => + Parallel.ForEach(fromRepsSteps, stepInfo => { - var isExtension = step.Kind == MaintenanceStepKind.LoadExtension; + var isExtension = stepInfo.Step.Kind == MaintenanceStepKind.LoadExtension; var extension = isExtension ? "cfe" : "cf"; - var crServer = _v8ServicesProvider.GetCrServerForPort(step.ConfigurationRepository!.Port); + var crServer = _v8ServicesProvider.GetCrServerForPort(stepInfo.ConfigurationRepository!.Port); var tempIbPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); try @@ -339,16 +460,16 @@ public class MaintenanceTaskExecutor : BackgroundService OnecV8BatchMode.CreateFileInfoBase(crServer.Platform, tempIbPath); var configPath = Path.Join(Path.GetTempPath(), $"{Guid.NewGuid()}.{extension}"); - var address = $"tcp://localhost:{crServer.Port}/{step.ConfigurationRepository.Name}"; + var address = $"tcp://localhost:{crServer.Port}/{stepInfo.ConfigurationRepository.Name}"; using var batch = OnecV8BatchMode.CreateDesignerBatch(crServer.Platform, tempIbPath); batch.DumpConfigRepository( configPath, address, - step.ConfigurationRepository.Credentials!.User, - step.ConfigurationRepository.Credentials!.Password); - - step.File = new FileDto + stepInfo.ConfigurationRepository.Credentials!.User, + stepInfo.ConfigurationRepository.Credentials!.Password); + + var file = new FileDto { Id = Guid.NewGuid(), Name = address, @@ -358,8 +479,12 @@ public class MaintenanceTaskExecutor : BackgroundService IsExtension = isExtension, Length = new FileInfo(configPath).Length }; - - files.Add(step.File.Id, configPath); + files.Add(file.Id, configPath); + + if (stepInfo.Step.Kind == MaintenanceStepKind.LoadConfiguration) + stepInfo.Step.LoadConfigurationStep!.File = file; + else if (stepInfo.Step.Kind == MaintenanceStepKind.LoadExtension) + stepInfo.Step.LoadExtensionStep!.File = file; } finally { @@ -401,22 +526,22 @@ public class MaintenanceTaskExecutor : BackgroundService private static async Task LockConnections(MaintenanceStepContext context) { await context.Rac.BlockConnections( - context.InfoBase.Cluster.ClusterInternalId, + context.InfoBase!.Cluster.ClusterInternalId, context.InfoBase.InfoBaseInternalId, - context.Step.AccessCode, - context.Step.Message, + context.Step.LockConnectionsStep!.AccessCode, + context.Step.LockConnectionsStep!.Message, context.InfoBase.Cluster.Credentials?.User ?? "", context.InfoBase.Cluster.Credentials?.Password ?? "", context.InfoBase.Credentials?.User ?? "", context.InfoBase.Credentials?.Password ?? ""); - context.AccessCode = context.Step.AccessCode; + context.AccessCode = context.Step.LockConnectionsStep.AccessCode; } private static async Task CloseConnections(MaintenanceStepContext context) { var sessions = await context.Rac.GetInfoBaseSessions( - context.InfoBase.Cluster.ClusterInternalId, + context.InfoBase!.Cluster.ClusterInternalId, context.InfoBase.InfoBaseInternalId, context.InfoBase.Cluster.Credentials?.User ?? "", context.InfoBase.Cluster.Credentials?.Password ?? "", @@ -447,7 +572,7 @@ public class MaintenanceTaskExecutor : BackgroundService private static async Task UnlockConnections(MaintenanceStepContext context) { await context.Rac.UnblockConnections( - context.InfoBase.Cluster.ClusterInternalId, + context.InfoBase!.Cluster.ClusterInternalId, context.InfoBase.InfoBaseInternalId, context.InfoBase.Cluster.Credentials?.User ?? "", context.InfoBase.Cluster.Credentials?.Password ?? "", @@ -457,14 +582,14 @@ public class MaintenanceTaskExecutor : BackgroundService private static async Task LoadExtension(MaintenanceStepContext context) { - var filePath = context.Files[context.Step.File!.Id]; + var filePath = context.Files[context.Step.LoadExtensionStep!.File!.Id]; if (context.UseDesignerAgent) { - await context.DesignerAgentClient!.LoadExtension(Path.GetFileName(filePath), context.Step.ExtensionName); - AddStepLogItem(context, $"Загрузка расширения \"{context.Step.ExtensionName}\" выполнена"); + await context.DesignerAgentClient!.LoadExtension(Path.GetFileName(filePath), context.Step.LoadExtensionStep.ExtensionName); + AddStepLogItem(context, $"Загрузка расширения \"{context.Step.LoadExtensionStep.ExtensionName}\" выполнена"); - context.DesignerAgentClient!.UpdateDbCfgExtension(context.Step.ExtensionName); + context.DesignerAgentClient!.UpdateDbCfgExtension(context.Step.LoadExtensionStep.ExtensionName); await context.DesignerAgentClient.ReadMessagesTillSuccess(message => LogDesignerAgentMessage(context, message)); } @@ -472,9 +597,9 @@ public class MaintenanceTaskExecutor : BackgroundService { using var batch = context.GetBatchDesigner(); batch.LoadExtension( - context.Step.ExtensionName, + context.Step.LoadExtensionStep.ExtensionName, filePath, - context.InfoBase.Credentials?.User ?? "", + context.InfoBase!.Credentials?.User ?? "", context.InfoBase.Credentials?.Password ?? "", context.AccessCode, true); @@ -485,10 +610,23 @@ public class MaintenanceTaskExecutor : BackgroundService private void ExecuteOneScript(MaintenanceStepContext context) { - var scriptPath = Directory.CreateTempSubdirectory().FullName; + string scriptPath; + string executable; + + if (context.Step.ExecuteOneScriptStep!.DebugMode) + { + executable = context.Step.ExecuteOneScriptStep!.ExecutablePath; + scriptPath = Path.GetDirectoryName(executable)!; + } + else + { + scriptPath = Directory.CreateTempSubdirectory().FullName; - var filePath = context.Files[context.Step.File!.Id]; - var opmMetadata = OneScriptPackageReader.Unzip(filePath, scriptPath); + var filePath = context.Files[context.Step.ExecuteOneScriptStep!.File.Id]; + var opmMetadata = OneScriptPackageReader.Unzip(filePath, scriptPath); + + executable = opmMetadata!.Executable; + } var scriptHost = new OneScriptExecutor(); scriptHost.OnEcho += (_, tuple) => @@ -499,17 +637,57 @@ public class MaintenanceTaskExecutor : BackgroundService { AddStepLogItem(context, ex.Message, true); }; - - var cmdParser = new Parser(); - var parsingResult = cmdParser.Parse(context.Step.CommandLineArguments); - scriptHost.ExecutePackageScript(scriptPath, opmMetadata!, [], e => + scriptHost.ExecutePackageScript(scriptPath, executable, [], e => { e.AddAssembly(typeof(OscriptIntegrationGlobalContext).Assembly); e.AddGlobalContext(_oscriptIntegrationGlobalContext); - }); + }, context.Step.ExecuteOneScriptStep!.DebugMode); - Directory.Delete(scriptPath, true); + if (!context.Step.ExecuteOneScriptStep!.DebugMode) + Directory.Delete(scriptPath, true); + } + + private async Task CopyInfoBase(MaintenanceStepContext context, CancellationToken cancellationToken) + { + var step = context.Step.CopyInfoBaseStep!; + + var sourceInfoBaseDetails = await GetInfoBaseDetails(step.SourceInfoBase); + var destinationInfoBaseDetails = await GetInfoBaseDetails(step.DestinationInfoBase); + + var canCopy = true; + + if (sourceInfoBaseDetails.Dbms != V8InfoBaseDbms.MsSqlServer) + { + AddStepLogItem(context, $"Тип базы-источника может быть только \"{destinationInfoBaseDetails.Dbms.GetDisplay()}\"", true); + canCopy = false; + } + + if (destinationInfoBaseDetails.Dbms != V8InfoBaseDbms.MsSqlServer) + { + AddStepLogItem(context, $"Тип базы-приемника может быть только \"{destinationInfoBaseDetails.Dbms.GetDisplay()}\"", true); + canCopy = false; + } + + if (canCopy) + { + var backupInfo = SqlHelper.GetLastBackupInfo(sourceInfoBaseDetails, step.SourceCredentials, cancellationToken); + } + } + + private async Task GetInfoBaseDetails(InfoBaseDto infoBase) + { + var ragent = _v8ServicesProvider.GetActiveRagentForClusterPort(infoBase.Cluster.Port); + var ras = _rasHolder.GetActiveRasForRagent(ragent); + var rac = Rac.GetRacForRasService(_racLogger, ras); + + return await rac.GetInfoBase( + infoBase.Cluster.ClusterInternalId, + infoBase.InfoBaseInternalId, + infoBase.Cluster.Credentials?.User ?? "", + infoBase.Cluster.Credentials?.Password ?? "", + infoBase.Credentials?.User ?? "", + infoBase.Credentials?.Password ?? ""); } private static async Task DeleteExtension(MaintenanceStepContext context) @@ -517,7 +695,7 @@ public class MaintenanceTaskExecutor : BackgroundService if (context.UseDesignerAgent) { var allExtensions = await context.DesignerAgentClient!.GetAllExtensions(); - var extensionsToDeleting = allExtensions.Where(c => Regex.IsMatch(c.Name, context.Step.ExtensionName)).ToList(); + var extensionsToDeleting = allExtensions.Where(c => Regex.IsMatch(c.Name, context.Step.DeleteExtensionStep!.ExtensionName)).ToList(); foreach (var extension in extensionsToDeleting) { @@ -529,12 +707,12 @@ public class MaintenanceTaskExecutor : BackgroundService { using var batchGet = context.GetBatchDesigner(); var allExtensions = batchGet.GetExtensionsList( - context.InfoBase.Credentials?.User ?? "", + context.InfoBase!.Credentials?.User ?? "", context.InfoBase.Credentials?.Password ?? "", context.AccessCode, true); - var extensionsToDeleting = allExtensions.Where(c => Regex.IsMatch(c, context.Step.ExtensionName)).ToList(); + var extensionsToDeleting = allExtensions.Where(c => Regex.IsMatch(c, context.Step.DeleteExtensionStep!.ExtensionName)).ToList(); foreach (var extension in extensionsToDeleting) { @@ -553,7 +731,7 @@ public class MaintenanceTaskExecutor : BackgroundService private static async Task LoadConfiguration(MaintenanceStepContext context) { - var filePath = context.Files[context.Step.File!.Id]; + var filePath = context.Files[context.Step.LoadConfigurationStep!.File!.Id]; if (context.UseDesignerAgent) { @@ -569,7 +747,7 @@ public class MaintenanceTaskExecutor : BackgroundService using var batch = context.GetBatchDesigner(); batch.LoadConfiguration( filePath, - context.InfoBase.Credentials?.User ?? "", + context.InfoBase!.Credentials?.User ?? "", context.InfoBase.Credentials?.Password ?? "", context.AccessCode, true); @@ -580,7 +758,7 @@ public class MaintenanceTaskExecutor : BackgroundService private static void UpdateConfiguration(MaintenanceStepContext context) { - var filePath = context.Files[context.Step.File!.Id]; + var filePath = context.Files[context.Step.UpdateConfigurationStep!.File.Id]; using var batch = context.GetBatchDesigner(); batch.UpdateConfiguration( @@ -595,12 +773,12 @@ public class MaintenanceTaskExecutor : BackgroundService private static void StartExternalDataProcessor(MaintenanceStepContext context) { - var filePath = context.Files[context.Step.File!.Id]; + var filePath = context.Files[context.Step.StartExternalDataProcessorStep!.File.Id]; var batch = context.GetBatchEnterprise(); batch.ExecuteExternalDataProcessor( filePath, - context.InfoBase.Credentials?.User ?? "", + context.InfoBase!.Credentials?.User ?? "", context.InfoBase.Credentials?.Password ?? "", context.AccessCode, true); diff --git a/oneswiss-agent/oneswiss-agent.csproj b/oneswiss-agent/oneswiss-agent.csproj index 936b34e..97eb855 100644 --- a/oneswiss-agent/oneswiss-agent.csproj +++ b/oneswiss-agent/oneswiss-agent.csproj @@ -33,6 +33,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -45,7 +46,7 @@ - + diff --git a/oneswiss-common/DTO/MaintenanceTasks/CopyInfoBaseStepDto.cs b/oneswiss-common/DTO/MaintenanceTasks/CopyInfoBaseStepDto.cs new file mode 100644 index 0000000..a75ca2e --- /dev/null +++ b/oneswiss-common/DTO/MaintenanceTasks/CopyInfoBaseStepDto.cs @@ -0,0 +1,16 @@ +using MessagePack; + +namespace OneSwiss.Common.DTO.MaintenanceTasks; + +[MessagePackObject] +public class CopyInfoBaseStepDto +{ + [Key(0)] + public CredentialsDto SourceCredentials { get; set; } + [Key(1)] + public InfoBaseDto SourceInfoBase { get; set; } + [Key(2)] + public CredentialsDto DestinationCredentials { get; set; } + [Key(3)] + public InfoBaseDto DestinationInfoBase { get; set; } +} \ No newline at end of file diff --git a/oneswiss-common/DTO/MaintenanceTasks/DeleteExtensionStepDto.cs b/oneswiss-common/DTO/MaintenanceTasks/DeleteExtensionStepDto.cs new file mode 100644 index 0000000..3da9847 --- /dev/null +++ b/oneswiss-common/DTO/MaintenanceTasks/DeleteExtensionStepDto.cs @@ -0,0 +1,10 @@ +using MessagePack; + +namespace OneSwiss.Common.DTO.MaintenanceTasks; + +[MessagePackObject] +public class DeleteExtensionStepDto +{ + [Key(0)] + public string ExtensionName { get; set; } +} \ No newline at end of file diff --git a/oneswiss-common/DTO/MaintenanceTasks/ExecuteOneScriptStepDto.cs b/oneswiss-common/DTO/MaintenanceTasks/ExecuteOneScriptStepDto.cs new file mode 100644 index 0000000..3f20b99 --- /dev/null +++ b/oneswiss-common/DTO/MaintenanceTasks/ExecuteOneScriptStepDto.cs @@ -0,0 +1,14 @@ +using MessagePack; + +namespace OneSwiss.Common.DTO.MaintenanceTasks; + +[MessagePackObject] +public class ExecuteOneScriptStepDto +{ + [Key(0)] + public FileDto File { get; set; } + [Key(1)] + public bool DebugMode { get; set; } + [Key(2)] + public string ExecutablePath { get; set; } +} \ No newline at end of file diff --git a/oneswiss-common/DTO/MaintenanceTasks/LoadConfigurationStepDto.cs b/oneswiss-common/DTO/MaintenanceTasks/LoadConfigurationStepDto.cs new file mode 100644 index 0000000..0a1b91f --- /dev/null +++ b/oneswiss-common/DTO/MaintenanceTasks/LoadConfigurationStepDto.cs @@ -0,0 +1,14 @@ +using MessagePack; + +namespace OneSwiss.Common.DTO.MaintenanceTasks; + +[MessagePackObject] +public class LoadConfigurationStepDto +{ + [Key(0)] + public bool FromConfigRepository { get; set; } + [Key(1)] + public FileDto? File { get; set; } + [Key(2)] + public ConfigurationRepositoryDto? ConfigurationRepository { get; set; } +} \ No newline at end of file diff --git a/oneswiss-common/DTO/MaintenanceTasks/LoadExtensionStepDto.cs b/oneswiss-common/DTO/MaintenanceTasks/LoadExtensionStepDto.cs new file mode 100644 index 0000000..04caf94 --- /dev/null +++ b/oneswiss-common/DTO/MaintenanceTasks/LoadExtensionStepDto.cs @@ -0,0 +1,16 @@ +using MessagePack; + +namespace OneSwiss.Common.DTO.MaintenanceTasks; + +[MessagePackObject] +public class LoadExtensionStepDto +{ + [Key(0)] + public string ExtensionName { get; set; } + [Key(1)] + public bool FromConfigRepository { get; set; } + [Key(2)] + public FileDto? File { get; set; } + [Key(3)] + public ConfigurationRepositoryDto? ConfigurationRepository { get; set; } +} \ No newline at end of file diff --git a/oneswiss-common/DTO/MaintenanceTasks/LockConnectionsStepDto.cs b/oneswiss-common/DTO/MaintenanceTasks/LockConnectionsStepDto.cs new file mode 100644 index 0000000..0bc106f --- /dev/null +++ b/oneswiss-common/DTO/MaintenanceTasks/LockConnectionsStepDto.cs @@ -0,0 +1,12 @@ +using MessagePack; + +namespace OneSwiss.Common.DTO.MaintenanceTasks; + +[MessagePackObject] +public class LockConnectionsStepDto +{ + [Key(0)] + public string AccessCode { get; set; } + [Key(1)] + public string Message { get; set; } +} \ No newline at end of file diff --git a/oneswiss-common/DTO/MaintenanceTasks/MaintenanceStepDto.cs b/oneswiss-common/DTO/MaintenanceTasks/MaintenanceStepDto.cs index 5ede48f..128c081 100644 --- a/oneswiss-common/DTO/MaintenanceTasks/MaintenanceStepDto.cs +++ b/oneswiss-common/DTO/MaintenanceTasks/MaintenanceStepDto.cs @@ -26,25 +26,20 @@ public class MaintenanceStepDto [ContextProperty("ИдентификаторПравогоШага", "RightStepId")] [Key(5)] public Guid? RightStepId { get; set; } - [ContextProperty("КодДоступа", "AccessCode")] - [Key(6)] - public string AccessCode { get; set; } = string.Empty; - [ContextProperty("Сообщение", "Message")] - [Key(7)] - public string Message { get; set; } = string.Empty; - [ContextProperty("АргументыКоманднойСтроки", "CommandLineArguments")] - [Key(8)] - public string CommandLineArguments { get; set; } = string.Empty; - [ContextProperty("Файл", "File")] + [Key(6)] + public CopyInfoBaseStepDto? CopyInfoBaseStep { get; set; } + [Key(7)] + public DeleteExtensionStepDto? DeleteExtensionStep { get; set; } + [Key(8)] + public ExecuteOneScriptStepDto? ExecuteOneScriptStep { get; set; } [Key(9)] - public FileDto? File { get; set; } - [ContextProperty("ИмяРасширения", "ExtensionName")] - [Key(10)] - public string ExtensionName { get; set; } = string.Empty; - [ContextProperty("ИзХранилищаКонфигураций", "FromConfigRepository")] + public LoadConfigurationStepDto? LoadConfigurationStep { get; set; } + [Key(10)] + public LoadExtensionStepDto? LoadExtensionStep { get; set; } [Key(11)] - public bool FromConfigRepository { get; set; } - [ContextProperty("ХранилищеКонфигураций", "ConfigRepository")] + public LockConnectionsStepDto? LockConnectionsStep { get; set; } [Key(12)] - public ConfigurationRepositoryDto? ConfigurationRepository { get; set; } + public StartExternalDataProcessorStepDto? StartExternalDataProcessorStep { get; set; } + [Key(13)] + public UpdateConfigurationStepDto? UpdateConfigurationStep { get; set; } } \ No newline at end of file diff --git a/oneswiss-common/DTO/MaintenanceTasks/MaintenanceTaskDto.cs b/oneswiss-common/DTO/MaintenanceTasks/MaintenanceTaskDto.cs index 44077fe..f6c48b7 100644 --- a/oneswiss-common/DTO/MaintenanceTasks/MaintenanceTaskDto.cs +++ b/oneswiss-common/DTO/MaintenanceTasks/MaintenanceTaskDto.cs @@ -3,17 +3,15 @@ using OneScript.Contexts; namespace OneSwiss.Common.DTO.MaintenanceTasks; -[ContextClass("ЗадачаОбслуживания", "MaintenanceTasks")] [MessagePackObject] public class MaintenanceTaskDto { - [ContextProperty("Идентификатор", "Id")] [Key(0)] public Guid Id { get; set; } = Guid.Empty; - [ContextProperty("Шаги", "Steps")] [Key(1)] + public bool CommonDestination { get; set; } + [Key(2)] public List Steps { get; set; } = []; - [ContextProperty("ИнформационныеБазы", "InfoBases")] - [Key(2)] + [Key(3)] public List InfoBases { get; set; } = []; } \ No newline at end of file diff --git a/oneswiss-common/DTO/MaintenanceTasks/StartExternalDataProcessorStepDto.cs b/oneswiss-common/DTO/MaintenanceTasks/StartExternalDataProcessorStepDto.cs new file mode 100644 index 0000000..9e43454 --- /dev/null +++ b/oneswiss-common/DTO/MaintenanceTasks/StartExternalDataProcessorStepDto.cs @@ -0,0 +1,10 @@ +using MessagePack; + +namespace OneSwiss.Common.DTO.MaintenanceTasks; + +[MessagePackObject] +public class StartExternalDataProcessorStepDto +{ + [Key(0)] + public FileDto File { get; set; } +} \ No newline at end of file diff --git a/oneswiss-common/DTO/MaintenanceTasks/UpdateConfigurationStepDto.cs b/oneswiss-common/DTO/MaintenanceTasks/UpdateConfigurationStepDto.cs new file mode 100644 index 0000000..54d8d9a --- /dev/null +++ b/oneswiss-common/DTO/MaintenanceTasks/UpdateConfigurationStepDto.cs @@ -0,0 +1,10 @@ +using MessagePack; + +namespace OneSwiss.Common.DTO.MaintenanceTasks; + +[MessagePackObject] +public class UpdateConfigurationStepDto +{ + [Key(0)] + public FileDto File { get; set; } +} \ No newline at end of file diff --git a/oneswiss-common/Models/MaintenanceTasks/MaintenanceStepKind.cs b/oneswiss-common/Models/MaintenanceTasks/MaintenanceStepKind.cs index 35ca226..baac927 100644 --- a/oneswiss-common/Models/MaintenanceTasks/MaintenanceStepKind.cs +++ b/oneswiss-common/Models/MaintenanceTasks/MaintenanceStepKind.cs @@ -21,5 +21,7 @@ public enum MaintenanceStepKind [Display(Name = "Запуск внешней обработки")] StartExternalDataProcessor, [Display(Name = "Выполнение скрипта - OneScript")] - ExecuteOneScript + ExecuteOneScript, + [Display(Name = "Копирование информационной базы")] + CopyInfoBase } \ No newline at end of file diff --git a/oneswiss-oscript-integration/OneScriptExecutor.cs b/oneswiss-oscript-integration/OneScriptExecutor.cs index 7f20850..54278c1 100644 --- a/oneswiss-oscript-integration/OneScriptExecutor.cs +++ b/oneswiss-oscript-integration/OneScriptExecutor.cs @@ -7,6 +7,7 @@ using OneSwiss.V8.Platform; using ScriptEngine; using ScriptEngine.HostedScript; using ScriptEngine.Hosting; +using ScriptEngine.Machine; using ExecutionContext = ScriptEngine.Machine.ExecutionContext; namespace OneSwiss.OneScript; @@ -17,38 +18,60 @@ public class OneScriptExecutor : IHostApplication public EventHandler? OnError = null; private string[] _args; - public void ExecutePackageScript(string path, OpmMetadata metadata, string[] args, Action engineBuilder) + public void ExecutePackageScript(string path, string executable, string[] args, Action engineBuilder, bool debugMode = false) { - _args = args; - var executablePath = Path.Combine(path, metadata.Executable); - var librariesPath = Path.Combine(path, "oscript_modules"); - - using var engine = CreateEngine(librariesPath, engineBuilder); + IDebugController? debugController = null; - var source = SourceCodeBuilder - .Create() - .FromFile(executablePath) - .Build(); + try + { + if (debugMode) + { + var debugServer = new TcpDebugServer(2801); + debugController = debugServer.CreateDebugController(); + } - var process = engine.CreateProcess(this, source); - var exitCode = process.Start(); + _args = args; + var executablePath = Path.Combine(path, executable); + var librariesPath = Path.Combine(path, "oscript_modules"); - if (exitCode != 0) - throw new Exception("Ошибка выполнения скрипта"); + using var engine = CreateEngine(librariesPath, engineBuilder, debugController); + + var source = SourceCodeBuilder + .Create() + .FromFile(executablePath) + .Build(); + + var process = engine.CreateProcess(this, source); + var exitCode = process.Start(); + + if (exitCode != 0) + throw new Exception("Ошибка выполнения скрипта"); + } + finally + { + debugController?.Dispose(); + } } - private static HostedScriptEngine CreateEngine(string librariesPath, Action engineBuilder) + private static HostedScriptEngine CreateEngine( + string librariesPath, + Action engineBuilder, + IDebugController? debugController = null) { var builder = DefaultEngineBuilder .Create() .SetDefaultOptions() - .UseImports() - .SetupEnvironment(e => - { - e.AddStandardLibrary(); - e.AddAssembly(typeof(OneScriptExecutor).Assembly); - engineBuilder.Invoke(e); - }); + .UseImports(); + + if (debugController != null) + builder.WithDebugger(debugController); + + builder.SetupEnvironment(e => + { + e.AddStandardLibrary(); + e.AddAssembly(typeof(OneScriptExecutor).Assembly); + engineBuilder.Invoke(e); + }); builder.Services.RegisterSingleton(new FileSystemDependencyResolver { diff --git a/oneswiss-oscript-integration/Oscript/RasServiceWrapper.cs b/oneswiss-oscript-integration/Oscript/RasServiceWrapper.cs index 85436ef..03dd792 100644 --- a/oneswiss-oscript-integration/Oscript/RasServiceWrapper.cs +++ b/oneswiss-oscript-integration/Oscript/RasServiceWrapper.cs @@ -20,7 +20,7 @@ public class RasServiceWrapper(RasService service) : AutoContext service.RagentHost; [ContextProperty("ПортRagent", "RagentPort", CanWrite = false)] - public int RagentPort => service.Port; + public int RagentPort => service.RagentPort; [ContextProperty("Порт", "Port", CanWrite = false)] public int Port => service.Port; diff --git a/oneswiss-oscript-integration/oneswiss-oscript-integration.csproj b/oneswiss-oscript-integration/oneswiss-oscript-integration.csproj index df1e216..8ef8c9b 100644 --- a/oneswiss-oscript-integration/oneswiss-oscript-integration.csproj +++ b/oneswiss-oscript-integration/oneswiss-oscript-integration.csproj @@ -8,8 +8,8 @@ - - + + diff --git a/oneswiss-server/AppDbContext.cs b/oneswiss-server/AppDbContext.cs index 177078f..e30ce59 100644 --- a/oneswiss-server/AppDbContext.cs +++ b/oneswiss-server/AppDbContext.cs @@ -24,6 +24,14 @@ namespace OneSwiss.Server public DbSet MaintenanceTasks { get; set; } public DbSet MaintenanceTaskLogs { get; set; } public DbSet MaintenanceSteps { get; set; } + public DbSet LockConnectionsSteps { get; set; } + public DbSet ExecuteOneScriptSteps { get; set; } + public DbSet StartExternalDataProcessorSteps { get; set; } + public DbSet UpdateConfigurationSteps { get; set; } + public DbSet LoadConfigurationSteps { get; set; } + public DbSet LoadExtensionSteps { get; set; } + public DbSet DeleteExtensionSteps { get; set; } + public DbSet CopyInfoBaseSteps { get; set; } public DbSet EventLogSettings { get; set; } public DbSet ErrorLoggingServiceSettings { get; set; } public DbSet ErrorReports { get; set; } @@ -73,4 +81,4 @@ namespace OneSwiss.Server await context.SaveChangesAsync(cancellationToken); } } -} +} \ No newline at end of file diff --git a/oneswiss-server/AutoMapper/DtoProfile.cs b/oneswiss-server/AutoMapper/DtoProfile.cs index 51d43d9..a7e6380 100644 --- a/oneswiss-server/AutoMapper/DtoProfile.cs +++ b/oneswiss-server/AutoMapper/DtoProfile.cs @@ -45,7 +45,16 @@ public class DtoProfile : Profile .ForMember(c => c.InfoBaseInternalId, opt => opt.MapFrom(src => src.Id)) .ForMember(c => c.InfoBaseName, opt => opt.MapFrom(src => src.Name)); + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); CreateMap().ReverseMap(); + CreateMap() .ForMember(c => c.InfoBases, opt => opt.MapFrom(src => src.InfoBases.Select(c => c.InfoBase))) .ReverseMap(); diff --git a/oneswiss-server/Components/ItemDeletingDialog.razor b/oneswiss-server/Components/ItemDeletingDialog.razor index cec6de7..9f197e5 100644 --- a/oneswiss-server/Components/ItemDeletingDialog.razor +++ b/oneswiss-server/Components/ItemDeletingDialog.razor @@ -2,6 +2,7 @@ @using Microsoft.EntityFrameworkCore @using OneSwiss.Server.Extensions @inject ISnackbar SnackBar +@inject ILogger> Logger @typeparam T where T : OneSwiss.Server.Models.DatabaseObject @@ -41,6 +42,7 @@ } catch (Exception e) { + Logger.LogError(e, "Ошибка при удалении элемента"); SnackBar.ShowError(e); Cancel(); } diff --git a/oneswiss-server/Components/Layout/NavMenu.razor b/oneswiss-server/Components/Layout/NavMenu.razor index 6de84ef..f2d6a7c 100644 --- a/oneswiss-server/Components/Layout/NavMenu.razor +++ b/oneswiss-server/Components/Layout/NavMenu.razor @@ -4,7 +4,7 @@ Панель - + Хранилища конфигураций Конфигурации обработки и скрипты Задачи обслуживания @@ -17,7 +17,7 @@ - + Настройки Учетные данные СУБД diff --git a/oneswiss-server/Components/Pages/MaintenanceTasks/Components/EditItem.razor b/oneswiss-server/Components/Pages/MaintenanceTasks/Components/EditItem.razor index 209ee22..558706f 100644 --- a/oneswiss-server/Components/Pages/MaintenanceTasks/Components/EditItem.razor +++ b/oneswiss-server/Components/Pages/MaintenanceTasks/Components/EditItem.razor @@ -41,18 +41,31 @@ OnUpdate="OnUpdate" Editable="_editable"> - - - + + + + @if (_editor.Model!.CommonDestination) + { + + } + else + { + + } - @foreach (var item in Enum.GetValues()) + @foreach (var item in GetAvailableStepKinds()) { @item.GetDisplay() } @@ -77,7 +90,7 @@ private readonly Validator _validator = new(); private EditDatabaseForm _editor = null!; private bool _editable; - + [Parameter] public Guid Id { get; set; } @@ -125,7 +138,17 @@ } private static IQueryable ModelSelector(IQueryable arg) - => arg.Include(c => c.InfoBases).ThenInclude(c => c.InfoBase).Include(c => c.Steps); + => arg + .Include(c => c.Agents) + .Include(c => c.InfoBases).ThenInclude(c => c.InfoBase) + .Include(c => c.Steps).ThenInclude(c => c.CopyInfoBaseStep) + .Include(c => c.Steps).ThenInclude(c => c.LoadExtensionStep) + .Include(c => c.Steps).ThenInclude(c => c.LoadConfigurationStep) + .Include(c => c.Steps).ThenInclude(c => c.UpdateConfigurationStep) + .Include(c => c.Steps).ThenInclude(c => c.DeleteExtensionStep) + .Include(c => c.Steps).ThenInclude(c => c.ExecuteOneScriptStep) + .Include(c => c.Steps).ThenInclude(c => c.LockConnectionsStep) + .Include(c => c.Steps).ThenInclude(c => c.StartExternalDataProcessorStep); private void BeforeValidation(MaintenanceTask obj) { @@ -141,6 +164,7 @@ private void OnUpdate(MaintenanceTask obj) { + ModelHelper.UpdateModelItems(_editor.Model!.Agents, obj.Agents); ModelHelper.UpdateModelItems(_editor.Model!.InfoBases, obj.InfoBases); ModelHelper.UpdateModelItems(_editor.Model!.Steps, obj.Steps); } @@ -152,13 +176,23 @@ var template = (await context.MaintenanceTasks .AsNoTracking() + .Include(c => c.Agents) .Include(c => c.InfoBases).ThenInclude(c => c.InfoBase) - .Include(c => c.Steps) + .Include(c => c.Steps).ThenInclude(c => c.CopyInfoBaseStep) + .Include(c => c.Steps).ThenInclude(c => c.LoadExtensionStep) + .Include(c => c.Steps).ThenInclude(c => c.LoadConfigurationStep) + .Include(c => c.Steps).ThenInclude(c => c.UpdateConfigurationStep) + .Include(c => c.Steps).ThenInclude(c => c.ExecuteOneScriptStep) + .Include(c => c.Steps).ThenInclude(c => c.StartExternalDataProcessorStep) + .Include(c => c.Steps).ThenInclude(c => c.DeleteExtensionStep) + .Include(c => c.Steps).ThenInclude(c => c.LockConnectionsStep) .FirstOrDefaultAsync(c => c.Id == TemplateId))!; task.Id = id; task.Description = template.Description; task.IsTemplate = IsTemplate; + task.CommonDestination = template.CommonDestination; + task.Agents = template.Agents; UpdateSelectedInfoBases(template); task.Steps.AddRange(template.Steps); @@ -178,6 +212,30 @@ if (i.RightStepId == currentId) i.RightStepId = newId; + + if (i.CopyInfoBaseStep != null) + i.CopyInfoBaseStep.Id = Guid.NewGuid(); + + if (i.DeleteExtensionStep != null) + i.DeleteExtensionStep.Id = Guid.NewGuid(); + + if (i.ExecuteOneScriptStep != null) + i.ExecuteOneScriptStep.Id = Guid.NewGuid(); + + if (i.LoadConfigurationStep != null) + i.LoadConfigurationStep.Id = Guid.NewGuid(); + + if (i.LoadExtensionStep != null) + i.LoadExtensionStep.Id = Guid.NewGuid(); + + if (i.LockConnectionsStep != null) + i.LockConnectionsStep.Id = Guid.NewGuid(); + + if (i.UpdateConfigurationStep != null) + i.UpdateConfigurationStep.Id = Guid.NewGuid(); + + if (i.StartExternalDataProcessorStep != null) + i.StartExternalDataProcessorStep.Id = Guid.NewGuid(); }); c.Id = newId; @@ -222,8 +280,20 @@ return; model.Changed += OnNodeChanged; - stepNode.Step.Id = Guid.NewGuid(); - }; + + InitNewNode(stepNode); + }; + } + + private static void InitNewNode(StepNode node) + { + node.Step.Id = Guid.NewGuid(); + + if (node.Step.Kind == MaintenanceStepKind.CopyInfoBase) + node.Step.CopyInfoBaseStep = new CopyInfoBaseStep + { + Id = Guid.NewGuid() + }; } private void DiagramToModel(MaintenanceTask task) @@ -285,7 +355,7 @@ foreach (var step in task.Steps) { var addedNode = Diagram.Nodes.Add(new StepNode(step, new Point(step.PositionX, step.PositionY))); - InitNode(addedNode, context); + InitNode(addedNode); nodeStepPairs.Add(step, addedNode); } @@ -382,22 +452,35 @@ private void OnClickNodeType(MaintenanceStepKind kind) { var node = new StepNode(kind); - InitNode(node, _editor.Context!); + InitNode(node); Diagram.Nodes.Add(node); } - private void InitNode(StepNode node, AppDbContext context) + private void InitNode(StepNode node) { - if (node.Step.NeedLoadFiles()) - node.Files = context.Files.AsNoTracking().Where(c => c.FileType == node.Step.AvailableFileType).ToList(); - - if (node.Step.IsConfigurationUpdating()) - node.Repositories = context.ConfigRepositories.AsNoTracking().Where(c => !c.Deleted && c.CredentialsId != null).ToList(); - node.AddPort(new StepPort(Diagram, node, PortAlignment.BottomRight)); node.AddPort(new StepPort(Diagram, node, PortAlignment.TopRight)); node.AddPort(new StepPort(Diagram, node, PortAlignment.Left)); } + + private MaintenanceStepKind[] GetAvailableStepKinds() + => _editor.Model!.CommonDestination switch + { + false => [ + MaintenanceStepKind.LockConnections, + MaintenanceStepKind.CloseConnections, + MaintenanceStepKind.UnlockConnections, + MaintenanceStepKind.LoadExtension, + MaintenanceStepKind.DeleteExtension, + MaintenanceStepKind.UpdateConfiguration, + MaintenanceStepKind.LoadConfiguration, + MaintenanceStepKind.StartExternalDataProcessor + ], + _ => [ + MaintenanceStepKind.ExecuteOneScript, + MaintenanceStepKind.CopyInfoBase + ] + }; class Validator : ModelValidator { @@ -415,37 +498,120 @@ When(c => !c.IsTemplate, () => { - RuleFor(c => c.InfoBases) - .NotEmpty() - .WithMessage("Не указано ни одной информационной базы"); - - RuleForEach(task => task.Steps) + When(c => !c.CommonDestination, () => + { + RuleFor(c => c.InfoBases) + .NotEmpty() + .WithMessage("Не указано ни одной информационной базы"); + }); + + RuleForEach(i => i.Steps) .Custom((step, context) => { - if (step.NeedLoadFiles() && step.FileId == Guid.Empty) + if (step.Kind == MaintenanceStepKind.DeleteExtension && string.IsNullOrEmpty(step.DeleteExtensionStep!.ExtensionName)) context.AddFailure( - nameof(context.PropertyPath), - $"В шаге \"{step.Kind.GetDisplay()}\" не указан файл"); - - if (step.NeedSpecifyConfigRepository() && step.ConfigurationRepositoryId == Guid.Empty) - context.AddFailure( - nameof(context.PropertyPath), - $"В шаге \"{step.Kind.GetDisplay()}\" не указано хранилище"); + nameof(context.PropertyPath), + $"В шаге \"{step.Kind.GetDisplay()}\" не указано регулярное выражение имени расширения"); - if (step.NeeSpecifyExtensionName() && string.IsNullOrEmpty(step.ExtensionName)) - context.AddFailure( - nameof(context.PropertyPath), - $"В шаге \"{step.Kind.GetDisplay()}\" не указано имя расширения"); + if (step.Kind == MaintenanceStepKind.ExecuteOneScript) + { + if (!step.ExecuteOneScriptStep!.DebugMode && step.ExecuteOneScriptStep!.FileId.EmptyOrNull()) + context.AddFailure( + nameof(context.PropertyPath), + $"В шаге \"{step.Kind.GetDisplay()}\" не указан файл .ospx"); + + if (step.ExecuteOneScriptStep!.DebugMode && string.IsNullOrEmpty(step.ExecuteOneScriptStep!.ExecutablePath)) + context.AddFailure( + nameof(context.PropertyPath), + $"В шаге \"{step.Kind.GetDisplay()}\" не указан файл скрипта"); + } + + if (step.Kind == MaintenanceStepKind.LoadExtension) + { + if (string.IsNullOrEmpty(step.LoadExtensionStep!.ExtensionName)) + context.AddFailure( + nameof(context.PropertyPath), + $"В шаге \"{step.Kind.GetDisplay()}\" не указано имя расширения"); + + if (step.LoadExtensionStep.FromConfigRepository) + { + if (step.LoadExtensionStep.ConfigurationRepositoryId.EmptyOrNull()) + context.AddFailure( + nameof(context.PropertyPath), + $"В шаге \"{step.Kind.GetDisplay()}\" не указано хранилище"); + } + else + { + if (step.LoadExtensionStep.FileId.EmptyOrNull()) + context.AddFailure( + nameof(context.PropertyPath), + $"В шаге \"{step.Kind.GetDisplay()}\" не указан файл .cfe"); + } + } - if (step.NeeSpecifyMessage() && string.IsNullOrEmpty(step.Message)) - context.AddFailure( - nameof(context.PropertyPath), - $"В шаге \"{step.Kind.GetDisplay()}\" не указан текст сообщения"); + if (step.Kind == MaintenanceStepKind.LoadConfiguration) + { + if (step.LoadConfigurationStep!.FromConfigRepository) + { + if (step.LoadExtensionStep!.ConfigurationRepositoryId.EmptyOrNull()) + context.AddFailure( + nameof(context.PropertyPath), + $"В шаге \"{step.Kind.GetDisplay()}\" не указано хранилище"); + } + else + { + if (step.LoadConfigurationStep.FileId.EmptyOrNull()) + context.AddFailure( + nameof(context.PropertyPath), + $"В шаге \"{step.Kind.GetDisplay()}\" не указан файл .cf"); + } + } - if (step.NeeSpecifyAccessCode() && string.IsNullOrEmpty(step.AccessCode)) + if (step.Kind == MaintenanceStepKind.UpdateConfiguration && step.UpdateConfigurationStep!.FileId.EmptyOrNull()) context.AddFailure( - nameof(context.PropertyPath), - $"В шаге \"{step.Kind.GetDisplay()}\" не указан код доступа"); + nameof(context.PropertyPath), + $"В шаге \"{step.Kind.GetDisplay()}\" не указан файл .cfu"); + + if (step.Kind == MaintenanceStepKind.StartExternalDataProcessor && step.StartExternalDataProcessorStep!.FileId.EmptyOrNull()) + context.AddFailure( + nameof(context.PropertyPath), + $"В шаге \"{step.Kind.GetDisplay()}\" не указан файл .epf"); + + if (step.Kind == MaintenanceStepKind.LockConnections) + { + if (string.IsNullOrEmpty(step.LockConnectionsStep!.AccessCode)) + context.AddFailure( + nameof(context.PropertyPath), + $"В шаге \"{step.Kind.GetDisplay()}\" не указан код доступа"); + + if (string.IsNullOrEmpty(step.LockConnectionsStep!.Message)) + context.AddFailure( + nameof(context.PropertyPath), + $"В шаге \"{step.Kind.GetDisplay()}\" не указано сообщение пользователю"); + } + + if (step.Kind == MaintenanceStepKind.CopyInfoBase) + { + if (step.CopyInfoBaseStep!.SourceCredentialsId.EmptyOrNull()) + context.AddFailure( + nameof(context.PropertyPath), + $"В шаге \"{step.Kind.GetDisplay()}\" не указаны учетные данные СУБД-источника"); + + if (step.CopyInfoBaseStep!.DestinationCredentialsId.EmptyOrNull()) + context.AddFailure( + nameof(context.PropertyPath), + $"В шаге \"{step.Kind.GetDisplay()}\" не указаны учетные данные СУБД-приемника"); + + if (step.CopyInfoBaseStep!.SourceInfoBaseId.EmptyOrNull()) + context.AddFailure( + nameof(context.PropertyPath), + $"В шаге \"{step.Kind.GetDisplay()}\" не указана ИБ-источник"); + + if (step.CopyInfoBaseStep!.DestinationInfoBaseId.EmptyOrNull()) + context.AddFailure( + nameof(context.PropertyPath), + $"В шаге \"{step.Kind.GetDisplay()}\" не указана ИБ-приемник"); + } }); }); } diff --git a/oneswiss-server/Components/Pages/MaintenanceTasks/Components/Index.razor b/oneswiss-server/Components/Pages/MaintenanceTasks/Components/Index.razor index 4cc8e46..6b1a947 100644 --- a/oneswiss-server/Components/Pages/MaintenanceTasks/Components/Index.razor +++ b/oneswiss-server/Components/Pages/MaintenanceTasks/Components/Index.razor @@ -74,7 +74,7 @@ [Parameter] public RenderFragment? RowActionsContent { get; set; } - public async Task CreateFromTemplate(MouseEventArgs obj) + public async Task CreateFromTemplate() { var options = new DialogOptions { CloseOnEscapeKey = true }; var dialog = await DialogService.ShowAsync("Выберите шаблон", options); @@ -83,6 +83,7 @@ if (result is { Canceled: false }) { var templateId = await dialog.GetReturnValueAsync(); + NavManager.NavigateTo($"{Route}/create?templateId={templateId}"); } } @@ -93,16 +94,41 @@ var task = await context.MaintenanceTasks .AsNoTracking() - .Include(c => c.Steps).ThenInclude(c => c.File) - .Include(c => c.Steps).ThenInclude(c => c.ConfigurationRepository).ThenInclude(c => c.Credentials) + + .Include(c => c.Steps).ThenInclude(c => c.LoadConfigurationStep.File) + .Include(c => c.Steps).ThenInclude(c => c.LoadConfigurationStep.ConfigurationRepository.Credentials) + + .Include(c => c.Steps).ThenInclude(c => c.LoadExtensionStep.File) + .Include(c => c.Steps).ThenInclude(c => c.LoadExtensionStep.ConfigurationRepository.Credentials) + + .Include(c => c.Steps).ThenInclude(c => c.UpdateConfigurationStep.File) + + .Include(c => c.Steps).ThenInclude(c => c.ExecuteOneScriptStep.File) + + .Include(c => c.Steps).ThenInclude(c => c.StartExternalDataProcessorStep.File) + + .Include(c => c.Steps).ThenInclude(c => c.CopyInfoBaseStep).ThenInclude(c => c.SourceCredentials) + .Include(c => c.Steps).ThenInclude(c => c.CopyInfoBaseStep).ThenInclude(c => c.SourceInfoBase.Credentials) + .Include(c => c.Steps).ThenInclude(c => c.CopyInfoBaseStep).ThenInclude(c => c.SourceInfoBase.Cluster.Credentials) + .Include(c => c.Steps).ThenInclude(c => c.CopyInfoBaseStep).ThenInclude(c => c.DestinationCredentials) + .Include(c => c.Steps).ThenInclude(c => c.CopyInfoBaseStep).ThenInclude(c => c.DestinationInfoBase.Credentials) + .Include(c => c.Steps).ThenInclude(c => c.CopyInfoBaseStep).ThenInclude(c => c.DestinationInfoBase.Cluster.Credentials) + + .Include(c => c.Agents) .Include(c => c.InfoBases).ThenInclude(c => c.InfoBase).ThenInclude(c => c.Credentials) .Include(c => c.InfoBases).ThenInclude(c => c.InfoBase).ThenInclude(c => c.Cluster).ThenInclude(c => c.Agent) .Include(c => c.InfoBases).ThenInclude(c => c.InfoBase).ThenInclude(c => c.Cluster).ThenInclude(c => c.Credentials) - .FirstOrDefaultAsync(c => c.Id == item.Id); + .FirstOrDefaultAsync(c => c.Id == item.Id); + + var taskAgents = task!.CommonDestination switch + { + false => task.InfoBases.Select(c => c.InfoBase.Cluster.Agent.Id).Distinct().ToList(), + true => task.Agents.Select(c => c.Id).Distinct().ToList() + }; var connections = await AgentsConnectionsManager.GetActiveAgentsConnections(); - foreach (var connection in connections) + foreach (var connection in connections.Where(c => taskAgents.Contains(c.AgentInstance!.Id))) await connection.StartMaintenanceTask(task!); var taskToUpdate = await context.MaintenanceTasks.FindAsync(task!.Id); diff --git a/oneswiss-server/Components/Pages/MaintenanceTasks/Log.razor b/oneswiss-server/Components/Pages/MaintenanceTasks/Log.razor index 8caead5..3525a6e 100644 --- a/oneswiss-server/Components/Pages/MaintenanceTasks/Log.razor +++ b/oneswiss-server/Components/Pages/MaintenanceTasks/Log.razor @@ -120,7 +120,7 @@ _taskLogItems = await context.MaintenanceTaskLogs .AsNoTracking() - .Where(c => c.TaskId == data.Id && c.StepId == null) + .Where(c => c.TaskId == data.Id && c.InfoBaseId == null) .OrderBy(c => c.TimeStamp) .ToListAsync(); diff --git a/oneswiss-server/Components/Pages/MaintenanceTasks/StepNode.cs b/oneswiss-server/Components/Pages/MaintenanceTasks/StepNode.cs index 7bd65a3..e469aa7 100644 --- a/oneswiss-server/Components/Pages/MaintenanceTasks/StepNode.cs +++ b/oneswiss-server/Components/Pages/MaintenanceTasks/StepNode.cs @@ -10,8 +10,6 @@ namespace OneSwiss.Server.Components.Pages.MaintenanceTasks; public class StepNode : NodeModel { public MaintenanceStep Step { get; set; } - public List Files { get; set; } = []; - public List Repositories { get; set; } = []; public StepNode(MaintenanceStepKind kind, Point? position = null) : base(position) { @@ -19,6 +17,63 @@ public class StepNode : NodeModel { Kind = kind }; + + switch (Step.Kind) + { + case MaintenanceStepKind.DeleteExtension: + Step.DeleteExtensionStep = new DeleteExtensionStep + { + Id = Guid.NewGuid() + }; + break; + case MaintenanceStepKind.ExecuteOneScript: + Step.ExecuteOneScriptStep = new ExecuteOneScriptStep + { + Id = Guid.NewGuid() + }; + break; + case MaintenanceStepKind.LockConnections: + Step.LockConnectionsStep = new LockConnectionsStep + { + Id = Guid.NewGuid() + }; + break; + case MaintenanceStepKind.LoadExtension: + Step.LoadExtensionStep = new LoadExtensionStep + { + Id = Guid.NewGuid() + }; + break; + case MaintenanceStepKind.UpdateConfiguration: + Step.UpdateConfigurationStep = new UpdateConfigurationStep + { + Id = Guid.NewGuid() + }; + break; + case MaintenanceStepKind.LoadConfiguration: + Step.LoadConfigurationStep = new LoadConfigurationStep + { + Id = Guid.NewGuid() + }; + break; + case MaintenanceStepKind.StartExternalDataProcessor: + Step.StartExternalDataProcessorStep = new StartExternalDataProcessorStep + { + Id = Guid.NewGuid() + }; + break; + case MaintenanceStepKind.CopyInfoBase: + Step.CopyInfoBaseStep = new CopyInfoBaseStep + { + Id = Guid.NewGuid() + }; + break; + case MaintenanceStepKind.CloseConnections: + case MaintenanceStepKind.UnlockConnections: + break; + default: + throw new ArgumentOutOfRangeException(); + } } public StepNode(MaintenanceStep step, Point? position = null) : base(position) diff --git a/oneswiss-server/Components/Pages/MaintenanceTasks/StepWidget.razor b/oneswiss-server/Components/Pages/MaintenanceTasks/StepWidget.razor index 48bee81..2b725a6 100644 --- a/oneswiss-server/Components/Pages/MaintenanceTasks/StepWidget.razor +++ b/oneswiss-server/Components/Pages/MaintenanceTasks/StepWidget.razor @@ -1,7 +1,13 @@ @using Blazor.Diagrams.Components.Renderers; +@using FluentValidation +@using Microsoft.EntityFrameworkCore @using OneSwiss.Common.Extensions @using OneSwiss.Common.Models.MaintenanceTasks @using OneSwiss.Server.Extensions +@using OneSwiss.Server.Models +@using OneSwiss.Server.Models.MaintenanceTasks +@inject IDbContextFactory ContextFactory +@implements IDisposable @@ -9,41 +15,136 @@ - @if (Node.Step.Kind == MaintenanceStepKind.LockConnections) + + @if (Node.Step.Kind == MaintenanceStepKind.StartExternalDataProcessor) { - - + + @foreach (var item in _context.Files.Where(c => c.FileType == FileType.Epf).ToList()) + { + @item.ToString() + } + } - @if (Node.Step.IsConfigurationUpdating()) + @if (Node.Step.Kind == MaintenanceStepKind.ExecuteOneScript) { - + + @if (Node.Step.ExecuteOneScriptStep.DebugMode) + { + + } + else + { + + @foreach (var item in _context.Files.Where(c => c.FileType == FileType.Ospx).ToList()) + { + @item.ToString() + } + + } } - - @if (Node.Step.NeeSpecifyExtensionName()) + + @if (Node.Step.Kind == MaintenanceStepKind.LockConnections) { - + + } - - @if (Node.Step.NeedSpecifyConfigRepository()) + + @if (Node.Step.Kind == MaintenanceStepKind.DeleteExtension) { - - @foreach (var item in Node.Repositories) + + } + + @if (Node.Step.Kind == MaintenanceStepKind.LoadConfiguration) + { + + + @if (Node.Step.LoadConfigurationStep.FromConfigRepository) + { + + @foreach (var item in _context.ConfigRepositories.ToList()) + { + @item.ToString() + } + + } + else + { + + @foreach (var item in _context.Files.Where(c => c.FileType == FileType.Cf).ToList()) + { + @item.ToString() + } + + } + } + + @if (Node.Step.Kind == MaintenanceStepKind.UpdateConfiguration) + { + + @foreach (var item in _context.Files.Where(c => c.FileType == FileType.Cfu).ToList()) { @item.ToString() } } - - @if (Node.Step.NeedLoadFiles()) + + @if (Node.Step.Kind == MaintenanceStepKind.LoadExtension) { - - @foreach (var item in Node.Files) + + + + @if (Node.Step.LoadExtensionStep.FromConfigRepository) + { + + @foreach (var item in _context.ConfigRepositories.ToList()) + { + @item.ToString() + } + + } + else + { + + @foreach (var item in _context.Files.Where(c => c.FileType == FileType.Cfe).ToList()) + { + @item.ToString() + } + + } + } + + @if (Node.Step.Kind == MaintenanceStepKind.CopyInfoBase) + { + + @foreach (var item in _context.Credentials.ToList()) { - @item.ToString() + @item.Name + } + + + + @foreach (var item in _context.InfoBases.ToList()) + { + @item.Name + } + + + + @foreach (var item in _context.Credentials.ToList()) + { + @item.Name + } + + + + @foreach (var item in _context.InfoBases.ToList()) + { + @item.Name } } + @foreach (var port in Node.Ports) @@ -54,9 +155,30 @@ @code { + private bool _needRender = false; + private AppDbContext? _context; + [Parameter] public StepNode Node { get; set; } = null!; + protected override async Task OnInitializedAsync() + { + _context = await ContextFactory.CreateDbContextAsync(); + } + protected override bool ShouldRender() - => false; + { + var result = _needRender; + _needRender = false; + + return result; + } + + public void Dispose() + { + _context?.Dispose(); + } + + private void SetNeedRender() + => _needRender = true; } \ No newline at end of file diff --git a/oneswiss-server/Components/Pages/MaintenanceTasks/Templates.razor b/oneswiss-server/Components/Pages/MaintenanceTasks/Templates.razor index 78be880..ba82279 100644 --- a/oneswiss-server/Components/Pages/MaintenanceTasks/Templates.razor +++ b/oneswiss-server/Components/Pages/MaintenanceTasks/Templates.razor @@ -40,9 +40,6 @@ item.Steps.ForEach(c => { c.MaintenanceTask = null!; - c.File = null!; - c.ConfigurationRepositoryId = Guid.Empty; - c.FileId = Guid.Empty; }); var jsonStr = JsonSerializer.Serialize(item, new JsonSerializerOptions diff --git a/oneswiss-server/Components/Shared/DatabaseObjectsTable.razor b/oneswiss-server/Components/Shared/DatabaseObjectsTable.razor index 2f93975..dca6240 100644 --- a/oneswiss-server/Components/Shared/DatabaseObjectsTable.razor +++ b/oneswiss-server/Components/Shared/DatabaseObjectsTable.razor @@ -18,7 +18,10 @@ @if (!DisableTableActions) { - Добавить + @if (ShowAddButton) + { + Добавить + } @if (TableActionsContent != null) { @TableActionsContent @@ -112,6 +115,9 @@ [Parameter] public string Route { get; set; } = string.Empty; + [Parameter] + public bool ShowAddButton { get; set; } = true; + [Parameter] public bool ShowDeleteButton { get; set; } = true; diff --git a/oneswiss-server/Components/Shared/EditDatabaseForm.razor b/oneswiss-server/Components/Shared/EditDatabaseForm.razor index 68c4880..8b4203f 100644 --- a/oneswiss-server/Components/Shared/EditDatabaseForm.razor +++ b/oneswiss-server/Components/Shared/EditDatabaseForm.razor @@ -153,9 +153,9 @@ var errors = await Validation.Invoke(Model!, item); var enumerable = errors.ToList(); - var valid = enumerable.Count == 0; + var addValid = enumerable.Count == 0; - if (valid) + if (addValid) continue; Form.IsValid = false; @@ -163,7 +163,7 @@ enumerable.ForEach(c => SnackBar.ShowError(c)); } } - + if (!Form.IsValid) { SnackBar.ShowError("Объект не записан"); diff --git a/oneswiss-server/Extensions/GuidExtensions.cs b/oneswiss-server/Extensions/GuidExtensions.cs new file mode 100644 index 0000000..9a6c8fc --- /dev/null +++ b/oneswiss-server/Extensions/GuidExtensions.cs @@ -0,0 +1,7 @@ +namespace OneSwiss.Server.Extensions; + +public static class GuidExtensions +{ + public static bool EmptyOrNull(this Guid? guid) + => guid is null || guid == Guid.Empty; +} \ No newline at end of file diff --git a/oneswiss-server/Extensions/MaintenanceStepKindExtensions.cs b/oneswiss-server/Extensions/MaintenanceStepKindExtensions.cs index 26a55b5..afc4d81 100644 --- a/oneswiss-server/Extensions/MaintenanceStepKindExtensions.cs +++ b/oneswiss-server/Extensions/MaintenanceStepKindExtensions.cs @@ -16,6 +16,7 @@ public static class MaintenanceStepKindExtensions MaintenanceStepKind.LoadConfiguration => "rgba(86, 179, 148, 0.7)", MaintenanceStepKind.StartExternalDataProcessor => "rgba(69, 175, 196, 0.7)", MaintenanceStepKind.ExecuteOneScript => "rgba(119, 160, 220, 0.7)", + MaintenanceStepKind.CopyInfoBase => "rgba(59, 160, 220, 0.7)", _ => throw new ArgumentOutOfRangeException(nameof(kind), kind, null) }; } \ No newline at end of file diff --git a/oneswiss-server/Migrations/20250801085631_Initial.Designer.cs b/oneswiss-server/Migrations/20250801085631_Initial.Designer.cs deleted file mode 100644 index 37febc1..0000000 --- a/oneswiss-server/Migrations/20250801085631_Initial.Designer.cs +++ /dev/null @@ -1,944 +0,0 @@ -// -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using OneSwiss.Server; - -#nullable disable - -namespace OneSwiss.Server.Migrations -{ - [DbContext(typeof(AppDbContext))] - [Migration("20250801085631_Initial")] - partial class Initial - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "9.0.7"); - - modelBuilder.Entity("AgentTechLogSeance", b => - { - b.Property("AgentsId") - .HasColumnType("TEXT"); - - b.Property("TechLogSeancesId") - .HasColumnType("TEXT"); - - b.HasKey("AgentsId", "TechLogSeancesId"); - - b.HasIndex("TechLogSeancesId"); - - b.ToTable("AgentTechLogSeance"); - }); - - modelBuilder.Entity("LogTemplateTechLogSeance", b => - { - b.Property("SeancesId") - .HasColumnType("TEXT"); - - b.Property("TemplatesId") - .HasColumnType("TEXT"); - - b.HasKey("SeancesId", "TemplatesId"); - - b.HasIndex("TemplatesId"); - - b.ToTable("LogTemplateTechLogSeance"); - }); - - modelBuilder.Entity("OneSwiss.Server.Models.Agent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("InstanceName") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Agents"); - }); - - modelBuilder.Entity("OneSwiss.Server.Models.Cluster", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("AgentId") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("ClusterInternalId") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("CredentialsId") - .HasColumnType("TEXT"); - - b.Property("Host") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("TEXT"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("TEXT"); - - b.Property("Port") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("AgentId"); - - b.HasIndex("CredentialsId"); - - b.ToTable("Clusters"); - }); - - modelBuilder.Entity("OneSwiss.Server.Models.ConfigurationRepository", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("AgentId") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("CredentialsId") - .HasColumnType("TEXT"); - - b.Property("Deleted") - .HasColumnType("INTEGER"); - - b.Property("Host") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("InternalId") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("Port") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("AgentId"); - - b.HasIndex("CredentialsId"); - - b.ToTable("ConfigRepositories"); - }); - - modelBuilder.Entity("OneSwiss.Server.Models.ConfigurationRepositoryUser", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("Deleted") - .HasColumnType("INTEGER"); - - b.Property("GitUser") - .HasColumnType("TEXT"); - - b.Property("InternalId") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("RepositoryId") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("RepositoryId"); - - b.ToTable("ConfigRepositoryUsers"); - }); - - modelBuilder.Entity("OneSwiss.Server.Models.Credentials", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("DefaultConfigRepositoriesAdmin") - .HasColumnType("INTEGER"); - - b.Property("DefaultForClusters") - .HasColumnType("INTEGER"); - - b.Property("DefaultV8Admin") - .HasColumnType("INTEGER"); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("Password") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("User") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Credentials"); - }); - - modelBuilder.Entity("OneSwiss.Server.Models.Dbms", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("Host") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("TEXT"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("TEXT"); - - b.Property("Port") - .HasColumnType("INTEGER"); - - b.Property("Type") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.ToTable("Dbms"); - }); - - modelBuilder.Entity("OneSwiss.Server.Models.ErrorLoggingServiceSettings", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("Enabled") - .HasColumnType("INTEGER"); - - b.Property("Message") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("ReportsTtl") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.ToTable("ErrorLoggingServiceSettings"); - }); - - modelBuilder.Entity("OneSwiss.Server.Models.ErrorReport", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("CreatedAt") - .HasColumnType("INTEGER"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("BLOB"); - - b.Property("Report") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("Screenshot") - .IsRequired() - .HasColumnType("BLOB"); - - b.HasKey("Id"); - - b.ToTable("ErrorReports"); - }); - - modelBuilder.Entity("OneSwiss.Server.Models.EventLogSettings", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("CredentialsId") - .HasColumnType("TEXT"); - - b.Property("DatabaseName") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("TEXT"); - - b.Property("DbmsId") - .HasColumnType("TEXT"); - - b.Property("Enabled") - .HasColumnType("INTEGER"); - - b.Property("InfoBaseNameRegex") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("Table") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("CredentialsId"); - - b.HasIndex("DbmsId"); - - b.ToTable("EventLogSettings"); - }); - - modelBuilder.Entity("OneSwiss.Server.Models.File", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("DataPath") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("FileType") - .HasColumnType("INTEGER"); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("Version") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Files"); - }); - - modelBuilder.Entity("OneSwiss.Server.Models.InfoBase", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("ClusterId") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("CredentialsId") - .HasColumnType("TEXT"); - - b.Property("InfoBaseInternalId") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("InfoBaseName") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("PublishAddress") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("ClusterId"); - - b.HasIndex("CredentialsId"); - - b.ToTable("InfoBases"); - }); - - modelBuilder.Entity("OneSwiss.Server.Models.LogTemplate", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("Content") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("LogTemplates"); - }); - - modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.InfoBaseMaintenanceTask", b => - { - b.Property("InfoBaseId") - .HasColumnType("TEXT"); - - b.Property("MaintenanceTaskId") - .HasColumnType("TEXT"); - - b.HasKey("InfoBaseId", "MaintenanceTaskId"); - - b.HasIndex("MaintenanceTaskId"); - - b.ToTable("InfoBaseMaintenanceTask"); - }); - - modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.MaintenanceStep", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("AccessCode") - .IsRequired() - .HasMaxLength(20) - .HasColumnType("TEXT"); - - b.Property("CommandLineArguments") - .IsRequired() - .HasMaxLength(1000) - .HasColumnType("TEXT"); - - b.Property("ConfigurationRepositoryId") - .HasColumnType("TEXT"); - - b.Property("ExtensionName") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("TEXT"); - - b.Property("FileId") - .HasColumnType("TEXT"); - - b.Property("FromConfigRepository") - .HasColumnType("INTEGER"); - - b.Property("Kind") - .HasColumnType("INTEGER"); - - b.Property("LeftStepId") - .HasColumnType("TEXT"); - - b.Property("MaintenanceTaskId") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("Message") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("TEXT"); - - b.Property("NodeKind") - .HasColumnType("INTEGER"); - - b.Property("PositionX") - .HasColumnType("REAL"); - - b.Property("PositionY") - .HasColumnType("REAL"); - - b.Property("PreviousStepId") - .HasColumnType("TEXT"); - - b.Property("RightStepId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("ConfigurationRepositoryId"); - - b.HasIndex("FileId"); - - b.HasIndex("MaintenanceTaskId"); - - b.ToTable("MaintenanceSteps"); - }); - - modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.MaintenanceTask", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("Description") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("TEXT"); - - b.Property("FinishDateTime") - .HasColumnType("INTEGER"); - - b.Property("IsFaulted") - .HasColumnType("INTEGER"); - - b.Property("IsTemplate") - .HasColumnType("INTEGER"); - - b.Property("StartDateTime") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.ToTable("MaintenanceTasks"); - }); - - modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.MaintenanceTaskLogItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("InfoBaseId") - .HasColumnType("TEXT"); - - b.Property("IsError") - .HasColumnType("INTEGER"); - - b.Property("IsFinish") - .HasColumnType("INTEGER"); - - b.Property("Message") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("TEXT"); - - b.Property("StepId") - .HasColumnType("TEXT"); - - b.Property("TaskId") - .HasColumnType("TEXT"); - - b.Property("TimeStamp") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("InfoBaseId"); - - b.HasIndex("StepId"); - - b.HasIndex("TaskId"); - - b.ToTable("MaintenanceTaskLogs"); - }); - - modelBuilder.Entity("OneSwiss.Server.Models.Notification", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("Additionalinfo") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("Channel") - .HasColumnType("INTEGER"); - - b.Property("CreatedAt") - .HasColumnType("INTEGER"); - - b.Property("Message") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("Recipient") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("Type") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.ToTable("Notifications"); - }); - - modelBuilder.Entity("OneSwiss.Server.Models.NotificationRecipient", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("Channel") - .HasColumnType("INTEGER"); - - b.PrimitiveCollection("NotificationTypes") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("SendTo") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("NotificationRecipients"); - }); - - modelBuilder.Entity("OneSwiss.Server.Models.TechLogFilter", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("Filter") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("TechLogFilters"); - }); - - modelBuilder.Entity("OneSwiss.Server.Models.TechLogSeance", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("Description") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("DirectSending") - .HasColumnType("INTEGER"); - - b.Property("Duration") - .HasColumnType("INTEGER"); - - b.Property("StartDateTime") - .HasColumnType("INTEGER"); - - b.Property("StartMode") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.ToTable("TechLogSeances"); - }); - - modelBuilder.Entity("OneSwiss.Server.Models.TechLogSettings", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("CredentialsId") - .HasColumnType("TEXT"); - - b.Property("DatabaseName") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("TEXT"); - - b.Property("DbmsId") - .HasColumnType("TEXT"); - - b.Property("Enabled") - .HasColumnType("INTEGER"); - - b.Property("Table") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("CredentialsId"); - - b.HasIndex("DbmsId"); - - b.ToTable("TechLogSettings"); - }); - - modelBuilder.Entity("OneSwiss.Server.Models.TelegramBotSettings", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("Token") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("TelegramBotSettings"); - }); - - modelBuilder.Entity("AgentTechLogSeance", b => - { - b.HasOne("OneSwiss.Server.Models.Agent", null) - .WithMany() - .HasForeignKey("AgentsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("OneSwiss.Server.Models.TechLogSeance", null) - .WithMany() - .HasForeignKey("TechLogSeancesId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("LogTemplateTechLogSeance", b => - { - b.HasOne("OneSwiss.Server.Models.TechLogSeance", null) - .WithMany() - .HasForeignKey("SeancesId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("OneSwiss.Server.Models.LogTemplate", null) - .WithMany() - .HasForeignKey("TemplatesId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("OneSwiss.Server.Models.Cluster", b => - { - b.HasOne("OneSwiss.Server.Models.Agent", "Agent") - .WithMany("Clusters") - .HasForeignKey("AgentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("OneSwiss.Server.Models.Credentials", "Credentials") - .WithMany("Clusters") - .HasForeignKey("CredentialsId") - .OnDelete(DeleteBehavior.SetNull); - - b.Navigation("Agent"); - - b.Navigation("Credentials"); - }); - - modelBuilder.Entity("OneSwiss.Server.Models.ConfigurationRepository", b => - { - b.HasOne("OneSwiss.Server.Models.Agent", "Agent") - .WithMany() - .HasForeignKey("AgentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("OneSwiss.Server.Models.Credentials", "Credentials") - .WithMany("ConfigurationRepositories") - .HasForeignKey("CredentialsId") - .OnDelete(DeleteBehavior.SetNull); - - b.Navigation("Agent"); - - b.Navigation("Credentials"); - }); - - modelBuilder.Entity("OneSwiss.Server.Models.ConfigurationRepositoryUser", b => - { - b.HasOne("OneSwiss.Server.Models.ConfigurationRepository", "Repository") - .WithMany("Users") - .HasForeignKey("RepositoryId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Repository"); - }); - - modelBuilder.Entity("OneSwiss.Server.Models.EventLogSettings", b => - { - b.HasOne("OneSwiss.Server.Models.Credentials", "Credentials") - .WithMany() - .HasForeignKey("CredentialsId"); - - b.HasOne("OneSwiss.Server.Models.Dbms", "Dbms") - .WithMany() - .HasForeignKey("DbmsId"); - - b.Navigation("Credentials"); - - b.Navigation("Dbms"); - }); - - modelBuilder.Entity("OneSwiss.Server.Models.InfoBase", b => - { - b.HasOne("OneSwiss.Server.Models.Cluster", "Cluster") - .WithMany("InfoBases") - .HasForeignKey("ClusterId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("OneSwiss.Server.Models.Credentials", "Credentials") - .WithMany("InfoBases") - .HasForeignKey("CredentialsId") - .OnDelete(DeleteBehavior.SetNull); - - b.Navigation("Cluster"); - - b.Navigation("Credentials"); - }); - - modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.InfoBaseMaintenanceTask", b => - { - b.HasOne("OneSwiss.Server.Models.InfoBase", "InfoBase") - .WithMany("MaintenanceTasks") - .HasForeignKey("InfoBaseId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("OneSwiss.Server.Models.MaintenanceTasks.MaintenanceTask", "MaintenanceTask") - .WithMany("InfoBases") - .HasForeignKey("MaintenanceTaskId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("InfoBase"); - - b.Navigation("MaintenanceTask"); - }); - - modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.MaintenanceStep", b => - { - b.HasOne("OneSwiss.Server.Models.ConfigurationRepository", "ConfigurationRepository") - .WithMany() - .HasForeignKey("ConfigurationRepositoryId"); - - b.HasOne("OneSwiss.Server.Models.File", "File") - .WithMany() - .HasForeignKey("FileId"); - - b.HasOne("OneSwiss.Server.Models.MaintenanceTasks.MaintenanceTask", "MaintenanceTask") - .WithMany("Steps") - .HasForeignKey("MaintenanceTaskId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ConfigurationRepository"); - - b.Navigation("File"); - - b.Navigation("MaintenanceTask"); - }); - - modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.MaintenanceTaskLogItem", b => - { - b.HasOne("OneSwiss.Server.Models.InfoBase", "InfoBase") - .WithMany() - .HasForeignKey("InfoBaseId") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("OneSwiss.Server.Models.MaintenanceTasks.MaintenanceStep", "Step") - .WithMany("Logs") - .HasForeignKey("StepId") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("OneSwiss.Server.Models.MaintenanceTasks.MaintenanceTask", "Task") - .WithMany("Logs") - .HasForeignKey("TaskId") - .OnDelete(DeleteBehavior.Cascade); - - b.Navigation("InfoBase"); - - b.Navigation("Step"); - - b.Navigation("Task"); - }); - - modelBuilder.Entity("OneSwiss.Server.Models.TechLogSettings", b => - { - b.HasOne("OneSwiss.Server.Models.Credentials", "Credentials") - .WithMany() - .HasForeignKey("CredentialsId"); - - b.HasOne("OneSwiss.Server.Models.Dbms", "Dbms") - .WithMany() - .HasForeignKey("DbmsId"); - - b.Navigation("Credentials"); - - b.Navigation("Dbms"); - }); - - modelBuilder.Entity("OneSwiss.Server.Models.Agent", b => - { - b.Navigation("Clusters"); - }); - - modelBuilder.Entity("OneSwiss.Server.Models.Cluster", b => - { - b.Navigation("InfoBases"); - }); - - modelBuilder.Entity("OneSwiss.Server.Models.ConfigurationRepository", b => - { - b.Navigation("Users"); - }); - - modelBuilder.Entity("OneSwiss.Server.Models.Credentials", b => - { - b.Navigation("Clusters"); - - b.Navigation("ConfigurationRepositories"); - - b.Navigation("InfoBases"); - }); - - modelBuilder.Entity("OneSwiss.Server.Models.InfoBase", b => - { - b.Navigation("MaintenanceTasks"); - }); - - modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.MaintenanceStep", b => - { - b.Navigation("Logs"); - }); - - modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.MaintenanceTask", b => - { - b.Navigation("InfoBases"); - - b.Navigation("Logs"); - - b.Navigation("Steps"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/oneswiss-server/Migrations/20250801194707_CustomNotifications.cs b/oneswiss-server/Migrations/20250801194707_CustomNotifications.cs deleted file mode 100644 index 4f7f79d..0000000 --- a/oneswiss-server/Migrations/20250801194707_CustomNotifications.cs +++ /dev/null @@ -1,56 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace OneSwiss.Server.Migrations -{ - /// - public partial class CustomNotifications : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "CustomNotificationsKeys", - table: "Notifications", - type: "TEXT", - nullable: false, - defaultValue: "[]"); - - migrationBuilder.CreateTable( - name: "CustomNotifications", - columns: table => new - { - Id = table.Column(type: "TEXT", nullable: false), - Key = table.Column(type: "TEXT", maxLength: 30, nullable: false), - Description = table.Column(type: "TEXT", nullable: false), - NotificationRecipientId = table.Column(type: "TEXT", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_CustomNotifications", x => x.Id); - table.ForeignKey( - name: "FK_CustomNotifications_NotificationRecipients_NotificationRecipientId", - column: x => x.NotificationRecipientId, - principalTable: "NotificationRecipients", - principalColumn: "Id"); - }); - - migrationBuilder.CreateIndex( - name: "IX_CustomNotifications_NotificationRecipientId", - table: "CustomNotifications", - column: "NotificationRecipientId"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "CustomNotifications"); - - migrationBuilder.DropColumn( - name: "CustomNotificationsKeys", - table: "Notifications"); - } - } -} diff --git a/oneswiss-server/Migrations/20250801194707_CustomNotifications.Designer.cs b/oneswiss-server/Migrations/20250803202021_Initial.Designer.cs similarity index 72% rename from oneswiss-server/Migrations/20250801194707_CustomNotifications.Designer.cs rename to oneswiss-server/Migrations/20250803202021_Initial.Designer.cs index 48d1e51..0ef7020 100644 --- a/oneswiss-server/Migrations/20250801194707_CustomNotifications.Designer.cs +++ b/oneswiss-server/Migrations/20250803202021_Initial.Designer.cs @@ -10,8 +10,8 @@ using OneSwiss.Server; namespace OneSwiss.Server.Migrations { [DbContext(typeof(AppDbContext))] - [Migration("20250801194707_CustomNotifications")] - partial class CustomNotifications + [Migration("20250803202021_Initial")] + partial class Initial { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -19,6 +19,21 @@ namespace OneSwiss.Server.Migrations #pragma warning disable 612, 618 modelBuilder.HasAnnotation("ProductVersion", "9.0.7"); + modelBuilder.Entity("AgentMaintenanceTask", b => + { + b.Property("AgentsId") + .HasColumnType("TEXT"); + + b.Property("MaintenanceTasksId") + .HasColumnType("TEXT"); + + b.HasKey("AgentsId", "MaintenanceTasksId"); + + b.HasIndex("MaintenanceTasksId"); + + b.ToTable("AgentMaintenanceTask"); + }); + modelBuilder.Entity("AgentTechLogSeance", b => { b.Property("AgentsId") @@ -427,6 +442,76 @@ namespace OneSwiss.Server.Migrations b.ToTable("LogTemplates"); }); + modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.CopyInfoBaseStep", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("DestinationCredentialsId") + .HasColumnType("TEXT"); + + b.Property("DestinationInfoBaseId") + .HasColumnType("TEXT"); + + b.Property("SourceCredentialsId") + .HasColumnType("TEXT"); + + b.Property("SourceInfoBaseId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("DestinationCredentialsId"); + + b.HasIndex("DestinationInfoBaseId"); + + b.HasIndex("SourceCredentialsId"); + + b.HasIndex("SourceInfoBaseId"); + + b.ToTable("CopyInfoBaseSteps"); + }); + + modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.DeleteExtensionStep", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ExtensionName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("DeleteExtensionSteps"); + }); + + modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.ExecuteOneScriptStep", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("DebugMode") + .HasColumnType("INTEGER"); + + b.Property("ExecutablePath") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("FileId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("FileId"); + + b.ToTable("ExecuteOneScriptSteps"); + }); + modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.InfoBaseMaintenanceTask", b => { b.Property("InfoBaseId") @@ -442,20 +527,34 @@ namespace OneSwiss.Server.Migrations b.ToTable("InfoBaseMaintenanceTask"); }); - modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.MaintenanceStep", b => + modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.LoadConfigurationStep", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("TEXT"); - b.Property("AccessCode") - .IsRequired() - .HasMaxLength(20) + b.Property("ConfigurationRepositoryId") .HasColumnType("TEXT"); - b.Property("CommandLineArguments") - .IsRequired() - .HasMaxLength(1000) + b.Property("FileId") + .HasColumnType("TEXT"); + + b.Property("FromConfigRepository") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ConfigurationRepositoryId"); + + b.HasIndex("FileId"); + + b.ToTable("LoadConfigurationSteps"); + }); + + modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.LoadExtensionStep", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() .HasColumnType("TEXT"); b.Property("ConfigurationRepositoryId") @@ -472,19 +571,68 @@ namespace OneSwiss.Server.Migrations b.Property("FromConfigRepository") .HasColumnType("INTEGER"); + b.HasKey("Id"); + + b.HasIndex("ConfigurationRepositoryId"); + + b.HasIndex("FileId"); + + b.ToTable("LoadExtensionSteps"); + }); + + modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.LockConnectionsStep", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AccessCode") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("TEXT"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("LockConnectionsSteps"); + }); + + modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.MaintenanceStep", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CopyInfoBaseStepId") + .HasColumnType("TEXT"); + + b.Property("DeleteExtensionStepId") + .HasColumnType("TEXT"); + + b.Property("ExecuteOneScriptStepId") + .HasColumnType("TEXT"); + b.Property("Kind") .HasColumnType("INTEGER"); b.Property("LeftStepId") .HasColumnType("TEXT"); - b.Property("MaintenanceTaskId") - .IsRequired() + b.Property("LoadConfigurationStepId") .HasColumnType("TEXT"); - b.Property("Message") + b.Property("LoadExtensionStepId") + .HasColumnType("TEXT"); + + b.Property("LockConnectionsStepId") + .HasColumnType("TEXT"); + + b.Property("MaintenanceTaskId") .IsRequired() - .HasMaxLength(200) .HasColumnType("TEXT"); b.Property("NodeKind") @@ -502,14 +650,32 @@ namespace OneSwiss.Server.Migrations b.Property("RightStepId") .HasColumnType("TEXT"); + b.Property("StartExternalDataProcessorStepId") + .HasColumnType("TEXT"); + + b.Property("UpdateConfigurationStepId") + .HasColumnType("TEXT"); + b.HasKey("Id"); - b.HasIndex("ConfigurationRepositoryId"); + b.HasIndex("CopyInfoBaseStepId"); - b.HasIndex("FileId"); + b.HasIndex("DeleteExtensionStepId"); + + b.HasIndex("ExecuteOneScriptStepId"); + + b.HasIndex("LoadConfigurationStepId"); + + b.HasIndex("LoadExtensionStepId"); + + b.HasIndex("LockConnectionsStepId"); b.HasIndex("MaintenanceTaskId"); + b.HasIndex("StartExternalDataProcessorStepId"); + + b.HasIndex("UpdateConfigurationStepId"); + b.ToTable("MaintenanceSteps"); }); @@ -519,6 +685,9 @@ namespace OneSwiss.Server.Migrations .ValueGeneratedOnAdd() .HasColumnType("TEXT"); + b.Property("CommonDestination") + .HasColumnType("INTEGER"); + b.Property("Description") .IsRequired() .HasMaxLength(200) @@ -581,6 +750,38 @@ namespace OneSwiss.Server.Migrations b.ToTable("MaintenanceTaskLogs"); }); + modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.StartExternalDataProcessorStep", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("FileId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("FileId"); + + b.ToTable("StartExternalDataProcessorSteps"); + }); + + modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.UpdateConfigurationStep", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("FileId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("FileId"); + + b.ToTable("UpdateConfigurationSteps"); + }); + modelBuilder.Entity("OneSwiss.Server.Models.Notification", b => { b.Property("Id") @@ -734,6 +935,21 @@ namespace OneSwiss.Server.Migrations b.ToTable("TelegramBotSettings"); }); + modelBuilder.Entity("AgentMaintenanceTask", b => + { + b.HasOne("OneSwiss.Server.Models.Agent", null) + .WithMany() + .HasForeignKey("AgentsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("OneSwiss.Server.Models.MaintenanceTasks.MaintenanceTask", null) + .WithMany() + .HasForeignKey("MaintenanceTasksId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + modelBuilder.Entity("AgentTechLogSeance", b => { b.HasOne("OneSwiss.Server.Models.Agent", null) @@ -851,6 +1067,42 @@ namespace OneSwiss.Server.Migrations b.Navigation("Credentials"); }); + modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.CopyInfoBaseStep", b => + { + b.HasOne("OneSwiss.Server.Models.Credentials", "DestinationCredentials") + .WithMany() + .HasForeignKey("DestinationCredentialsId"); + + b.HasOne("OneSwiss.Server.Models.InfoBase", "DestinationInfoBase") + .WithMany() + .HasForeignKey("DestinationInfoBaseId"); + + b.HasOne("OneSwiss.Server.Models.Credentials", "SourceCredentials") + .WithMany() + .HasForeignKey("SourceCredentialsId"); + + b.HasOne("OneSwiss.Server.Models.InfoBase", "SourceInfoBase") + .WithMany() + .HasForeignKey("SourceInfoBaseId"); + + b.Navigation("DestinationCredentials"); + + b.Navigation("DestinationInfoBase"); + + b.Navigation("SourceCredentials"); + + b.Navigation("SourceInfoBase"); + }); + + modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.ExecuteOneScriptStep", b => + { + b.HasOne("OneSwiss.Server.Models.File", "File") + .WithMany() + .HasForeignKey("FileId"); + + b.Navigation("File"); + }); + modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.InfoBaseMaintenanceTask", b => { b.HasOne("OneSwiss.Server.Models.InfoBase", "InfoBase") @@ -870,7 +1122,7 @@ namespace OneSwiss.Server.Migrations b.Navigation("MaintenanceTask"); }); - modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.MaintenanceStep", b => + modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.LoadConfigurationStep", b => { b.HasOne("OneSwiss.Server.Models.ConfigurationRepository", "ConfigurationRepository") .WithMany() @@ -880,17 +1132,83 @@ namespace OneSwiss.Server.Migrations .WithMany() .HasForeignKey("FileId"); + b.Navigation("ConfigurationRepository"); + + b.Navigation("File"); + }); + + modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.LoadExtensionStep", b => + { + b.HasOne("OneSwiss.Server.Models.ConfigurationRepository", "ConfigurationRepository") + .WithMany() + .HasForeignKey("ConfigurationRepositoryId"); + + b.HasOne("OneSwiss.Server.Models.File", "File") + .WithMany() + .HasForeignKey("FileId"); + + b.Navigation("ConfigurationRepository"); + + b.Navigation("File"); + }); + + modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.MaintenanceStep", b => + { + b.HasOne("OneSwiss.Server.Models.MaintenanceTasks.CopyInfoBaseStep", "CopyInfoBaseStep") + .WithMany() + .HasForeignKey("CopyInfoBaseStepId"); + + b.HasOne("OneSwiss.Server.Models.MaintenanceTasks.DeleteExtensionStep", "DeleteExtensionStep") + .WithMany() + .HasForeignKey("DeleteExtensionStepId"); + + b.HasOne("OneSwiss.Server.Models.MaintenanceTasks.ExecuteOneScriptStep", "ExecuteOneScriptStep") + .WithMany() + .HasForeignKey("ExecuteOneScriptStepId"); + + b.HasOne("OneSwiss.Server.Models.MaintenanceTasks.LoadConfigurationStep", "LoadConfigurationStep") + .WithMany() + .HasForeignKey("LoadConfigurationStepId"); + + b.HasOne("OneSwiss.Server.Models.MaintenanceTasks.LoadExtensionStep", "LoadExtensionStep") + .WithMany() + .HasForeignKey("LoadExtensionStepId"); + + b.HasOne("OneSwiss.Server.Models.MaintenanceTasks.LockConnectionsStep", "LockConnectionsStep") + .WithMany() + .HasForeignKey("LockConnectionsStepId"); + b.HasOne("OneSwiss.Server.Models.MaintenanceTasks.MaintenanceTask", "MaintenanceTask") .WithMany("Steps") .HasForeignKey("MaintenanceTaskId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.Navigation("ConfigurationRepository"); + b.HasOne("OneSwiss.Server.Models.MaintenanceTasks.StartExternalDataProcessorStep", "StartExternalDataProcessorStep") + .WithMany() + .HasForeignKey("StartExternalDataProcessorStepId"); - b.Navigation("File"); + b.HasOne("OneSwiss.Server.Models.MaintenanceTasks.UpdateConfigurationStep", "UpdateConfigurationStep") + .WithMany() + .HasForeignKey("UpdateConfigurationStepId"); + + b.Navigation("CopyInfoBaseStep"); + + b.Navigation("DeleteExtensionStep"); + + b.Navigation("ExecuteOneScriptStep"); + + b.Navigation("LoadConfigurationStep"); + + b.Navigation("LoadExtensionStep"); + + b.Navigation("LockConnectionsStep"); b.Navigation("MaintenanceTask"); + + b.Navigation("StartExternalDataProcessorStep"); + + b.Navigation("UpdateConfigurationStep"); }); modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.MaintenanceTaskLogItem", b => @@ -917,6 +1235,24 @@ namespace OneSwiss.Server.Migrations b.Navigation("Task"); }); + modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.StartExternalDataProcessorStep", b => + { + b.HasOne("OneSwiss.Server.Models.File", "File") + .WithMany() + .HasForeignKey("FileId"); + + b.Navigation("File"); + }); + + modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.UpdateConfigurationStep", b => + { + b.HasOne("OneSwiss.Server.Models.File", "File") + .WithMany() + .HasForeignKey("FileId"); + + b.Navigation("File"); + }); + modelBuilder.Entity("OneSwiss.Server.Models.TechLogSettings", b => { b.HasOne("OneSwiss.Server.Models.Credentials", "Credentials") diff --git a/oneswiss-server/Migrations/20250801085631_Initial.cs b/oneswiss-server/Migrations/20250803202021_Initial.cs similarity index 64% rename from oneswiss-server/Migrations/20250801085631_Initial.cs rename to oneswiss-server/Migrations/20250803202021_Initial.cs index ddeb8b6..17e7c1f 100644 --- a/oneswiss-server/Migrations/20250801085631_Initial.cs +++ b/oneswiss-server/Migrations/20250803202021_Initial.cs @@ -54,6 +54,18 @@ namespace OneSwiss.Server.Migrations table.PrimaryKey("PK_Dbms", x => x.Id); }); + migrationBuilder.CreateTable( + name: "DeleteExtensionSteps", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + ExtensionName = table.Column(type: "TEXT", maxLength: 200, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_DeleteExtensionSteps", x => x.Id); + }); + migrationBuilder.CreateTable( name: "ErrorLoggingServiceSettings", columns: table => new @@ -98,6 +110,19 @@ namespace OneSwiss.Server.Migrations table.PrimaryKey("PK_Files", x => x.Id); }); + migrationBuilder.CreateTable( + name: "LockConnectionsSteps", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + AccessCode = table.Column(type: "TEXT", maxLength: 20, nullable: false), + Message = table.Column(type: "TEXT", maxLength: 200, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_LockConnectionsSteps", x => x.Id); + }); + migrationBuilder.CreateTable( name: "LogTemplates", columns: table => new @@ -120,7 +145,8 @@ namespace OneSwiss.Server.Migrations StartDateTime = table.Column(type: "INTEGER", nullable: false), IsFaulted = table.Column(type: "INTEGER", nullable: false), FinishDateTime = table.Column(type: "INTEGER", nullable: false), - IsTemplate = table.Column(type: "INTEGER", nullable: false) + IsTemplate = table.Column(type: "INTEGER", nullable: false), + CommonDestination = table.Column(type: "INTEGER", nullable: false) }, constraints: table => { @@ -151,7 +177,8 @@ namespace OneSwiss.Server.Migrations Message = table.Column(type: "TEXT", nullable: false), Channel = table.Column(type: "INTEGER", nullable: false), Recipient = table.Column(type: "TEXT", nullable: false), - Additionalinfo = table.Column(type: "TEXT", nullable: false) + Additionalinfo = table.Column(type: "TEXT", nullable: false), + CustomNotificationsKeys = table.Column(type: "TEXT", nullable: false) }, constraints: table => { @@ -311,6 +338,102 @@ namespace OneSwiss.Server.Migrations principalColumn: "Id"); }); + migrationBuilder.CreateTable( + name: "ExecuteOneScriptSteps", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + DebugMode = table.Column(type: "INTEGER", nullable: false), + ExecutablePath = table.Column(type: "TEXT", nullable: false), + FileId = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_ExecuteOneScriptSteps", x => x.Id); + table.ForeignKey( + name: "FK_ExecuteOneScriptSteps_Files_FileId", + column: x => x.FileId, + principalTable: "Files", + principalColumn: "Id"); + }); + + migrationBuilder.CreateTable( + name: "StartExternalDataProcessorSteps", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + FileId = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_StartExternalDataProcessorSteps", x => x.Id); + table.ForeignKey( + name: "FK_StartExternalDataProcessorSteps_Files_FileId", + column: x => x.FileId, + principalTable: "Files", + principalColumn: "Id"); + }); + + migrationBuilder.CreateTable( + name: "UpdateConfigurationSteps", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + FileId = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_UpdateConfigurationSteps", x => x.Id); + table.ForeignKey( + name: "FK_UpdateConfigurationSteps_Files_FileId", + column: x => x.FileId, + principalTable: "Files", + principalColumn: "Id"); + }); + + migrationBuilder.CreateTable( + name: "AgentMaintenanceTask", + columns: table => new + { + AgentsId = table.Column(type: "TEXT", nullable: false), + MaintenanceTasksId = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AgentMaintenanceTask", x => new { x.AgentsId, x.MaintenanceTasksId }); + table.ForeignKey( + name: "FK_AgentMaintenanceTask_Agents_AgentsId", + column: x => x.AgentsId, + principalTable: "Agents", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_AgentMaintenanceTask_MaintenanceTasks_MaintenanceTasksId", + column: x => x.MaintenanceTasksId, + principalTable: "MaintenanceTasks", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "CustomNotifications", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + Key = table.Column(type: "TEXT", maxLength: 30, nullable: false), + Description = table.Column(type: "TEXT", nullable: false), + NotificationRecipientId = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_CustomNotifications", x => x.Id); + table.ForeignKey( + name: "FK_CustomNotifications_NotificationRecipients_NotificationRecipientId", + column: x => x.NotificationRecipientId, + principalTable: "NotificationRecipients", + principalColumn: "Id"); + }); + migrationBuilder.CreateTable( name: "AgentTechLogSeance", columns: table => new @@ -411,45 +534,87 @@ namespace OneSwiss.Server.Migrations }); migrationBuilder.CreateTable( - name: "MaintenanceSteps", + name: "LoadConfigurationSteps", columns: table => new { Id = table.Column(type: "TEXT", nullable: false), - MaintenanceTaskId = table.Column(type: "TEXT", nullable: false), - Kind = table.Column(type: "INTEGER", nullable: false), - NodeKind = table.Column(type: "INTEGER", nullable: false), - PreviousStepId = table.Column(type: "TEXT", nullable: true), - LeftStepId = table.Column(type: "TEXT", nullable: true), - RightStepId = table.Column(type: "TEXT", nullable: true), - AccessCode = table.Column(type: "TEXT", maxLength: 20, nullable: false), - Message = table.Column(type: "TEXT", maxLength: 200, nullable: false), - CommandLineArguments = table.Column(type: "TEXT", maxLength: 1000, nullable: false), FromConfigRepository = table.Column(type: "INTEGER", nullable: false), - ConfigurationRepositoryId = table.Column(type: "TEXT", nullable: true), FileId = table.Column(type: "TEXT", nullable: true), - ExtensionName = table.Column(type: "TEXT", maxLength: 200, nullable: false), - PositionX = table.Column(type: "REAL", nullable: false), - PositionY = table.Column(type: "REAL", nullable: false) + ConfigurationRepositoryId = table.Column(type: "TEXT", nullable: true) }, constraints: table => { - table.PrimaryKey("PK_MaintenanceSteps", x => x.Id); + table.PrimaryKey("PK_LoadConfigurationSteps", x => x.Id); table.ForeignKey( - name: "FK_MaintenanceSteps_ConfigRepositories_ConfigurationRepositoryId", + name: "FK_LoadConfigurationSteps_ConfigRepositories_ConfigurationRepositoryId", column: x => x.ConfigurationRepositoryId, principalTable: "ConfigRepositories", principalColumn: "Id"); table.ForeignKey( - name: "FK_MaintenanceSteps_Files_FileId", + name: "FK_LoadConfigurationSteps_Files_FileId", column: x => x.FileId, principalTable: "Files", principalColumn: "Id"); + }); + + migrationBuilder.CreateTable( + name: "LoadExtensionSteps", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + FromConfigRepository = table.Column(type: "INTEGER", nullable: false), + ExtensionName = table.Column(type: "TEXT", maxLength: 200, nullable: false), + FileId = table.Column(type: "TEXT", nullable: true), + ConfigurationRepositoryId = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_LoadExtensionSteps", x => x.Id); table.ForeignKey( - name: "FK_MaintenanceSteps_MaintenanceTasks_MaintenanceTaskId", - column: x => x.MaintenanceTaskId, - principalTable: "MaintenanceTasks", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); + name: "FK_LoadExtensionSteps_ConfigRepositories_ConfigurationRepositoryId", + column: x => x.ConfigurationRepositoryId, + principalTable: "ConfigRepositories", + principalColumn: "Id"); + table.ForeignKey( + name: "FK_LoadExtensionSteps_Files_FileId", + column: x => x.FileId, + principalTable: "Files", + principalColumn: "Id"); + }); + + migrationBuilder.CreateTable( + name: "CopyInfoBaseSteps", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + SourceCredentialsId = table.Column(type: "TEXT", nullable: true), + SourceInfoBaseId = table.Column(type: "TEXT", nullable: true), + DestinationCredentialsId = table.Column(type: "TEXT", nullable: true), + DestinationInfoBaseId = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_CopyInfoBaseSteps", x => x.Id); + table.ForeignKey( + name: "FK_CopyInfoBaseSteps_Credentials_DestinationCredentialsId", + column: x => x.DestinationCredentialsId, + principalTable: "Credentials", + principalColumn: "Id"); + table.ForeignKey( + name: "FK_CopyInfoBaseSteps_Credentials_SourceCredentialsId", + column: x => x.SourceCredentialsId, + principalTable: "Credentials", + principalColumn: "Id"); + table.ForeignKey( + name: "FK_CopyInfoBaseSteps_InfoBases_DestinationInfoBaseId", + column: x => x.DestinationInfoBaseId, + principalTable: "InfoBases", + principalColumn: "Id"); + table.ForeignKey( + name: "FK_CopyInfoBaseSteps_InfoBases_SourceInfoBaseId", + column: x => x.SourceInfoBaseId, + principalTable: "InfoBases", + principalColumn: "Id"); }); migrationBuilder.CreateTable( @@ -476,6 +641,79 @@ namespace OneSwiss.Server.Migrations onDelete: ReferentialAction.Cascade); }); + migrationBuilder.CreateTable( + name: "MaintenanceSteps", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + MaintenanceTaskId = table.Column(type: "TEXT", nullable: false), + Kind = table.Column(type: "INTEGER", nullable: false), + NodeKind = table.Column(type: "INTEGER", nullable: false), + PreviousStepId = table.Column(type: "TEXT", nullable: true), + LeftStepId = table.Column(type: "TEXT", nullable: true), + RightStepId = table.Column(type: "TEXT", nullable: true), + LockConnectionsStepId = table.Column(type: "TEXT", nullable: true), + LoadConfigurationStepId = table.Column(type: "TEXT", nullable: true), + DeleteExtensionStepId = table.Column(type: "TEXT", nullable: true), + LoadExtensionStepId = table.Column(type: "TEXT", nullable: true), + UpdateConfigurationStepId = table.Column(type: "TEXT", nullable: true), + StartExternalDataProcessorStepId = table.Column(type: "TEXT", nullable: true), + ExecuteOneScriptStepId = table.Column(type: "TEXT", nullable: true), + CopyInfoBaseStepId = table.Column(type: "TEXT", nullable: true), + PositionX = table.Column(type: "REAL", nullable: false), + PositionY = table.Column(type: "REAL", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_MaintenanceSteps", x => x.Id); + table.ForeignKey( + name: "FK_MaintenanceSteps_CopyInfoBaseSteps_CopyInfoBaseStepId", + column: x => x.CopyInfoBaseStepId, + principalTable: "CopyInfoBaseSteps", + principalColumn: "Id"); + table.ForeignKey( + name: "FK_MaintenanceSteps_DeleteExtensionSteps_DeleteExtensionStepId", + column: x => x.DeleteExtensionStepId, + principalTable: "DeleteExtensionSteps", + principalColumn: "Id"); + table.ForeignKey( + name: "FK_MaintenanceSteps_ExecuteOneScriptSteps_ExecuteOneScriptStepId", + column: x => x.ExecuteOneScriptStepId, + principalTable: "ExecuteOneScriptSteps", + principalColumn: "Id"); + table.ForeignKey( + name: "FK_MaintenanceSteps_LoadConfigurationSteps_LoadConfigurationStepId", + column: x => x.LoadConfigurationStepId, + principalTable: "LoadConfigurationSteps", + principalColumn: "Id"); + table.ForeignKey( + name: "FK_MaintenanceSteps_LoadExtensionSteps_LoadExtensionStepId", + column: x => x.LoadExtensionStepId, + principalTable: "LoadExtensionSteps", + principalColumn: "Id"); + table.ForeignKey( + name: "FK_MaintenanceSteps_LockConnectionsSteps_LockConnectionsStepId", + column: x => x.LockConnectionsStepId, + principalTable: "LockConnectionsSteps", + principalColumn: "Id"); + table.ForeignKey( + name: "FK_MaintenanceSteps_MaintenanceTasks_MaintenanceTaskId", + column: x => x.MaintenanceTaskId, + principalTable: "MaintenanceTasks", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_MaintenanceSteps_StartExternalDataProcessorSteps_StartExternalDataProcessorStepId", + column: x => x.StartExternalDataProcessorStepId, + principalTable: "StartExternalDataProcessorSteps", + principalColumn: "Id"); + table.ForeignKey( + name: "FK_MaintenanceSteps_UpdateConfigurationSteps_UpdateConfigurationStepId", + column: x => x.UpdateConfigurationStepId, + principalTable: "UpdateConfigurationSteps", + principalColumn: "Id"); + }); + migrationBuilder.CreateTable( name: "MaintenanceTaskLogs", columns: table => new @@ -512,6 +750,11 @@ namespace OneSwiss.Server.Migrations onDelete: ReferentialAction.Cascade); }); + migrationBuilder.CreateIndex( + name: "IX_AgentMaintenanceTask_MaintenanceTasksId", + table: "AgentMaintenanceTask", + column: "MaintenanceTasksId"); + migrationBuilder.CreateIndex( name: "IX_AgentTechLogSeance_TechLogSeancesId", table: "AgentTechLogSeance", @@ -542,6 +785,31 @@ namespace OneSwiss.Server.Migrations table: "ConfigRepositoryUsers", column: "RepositoryId"); + migrationBuilder.CreateIndex( + name: "IX_CopyInfoBaseSteps_DestinationCredentialsId", + table: "CopyInfoBaseSteps", + column: "DestinationCredentialsId"); + + migrationBuilder.CreateIndex( + name: "IX_CopyInfoBaseSteps_DestinationInfoBaseId", + table: "CopyInfoBaseSteps", + column: "DestinationInfoBaseId"); + + migrationBuilder.CreateIndex( + name: "IX_CopyInfoBaseSteps_SourceCredentialsId", + table: "CopyInfoBaseSteps", + column: "SourceCredentialsId"); + + migrationBuilder.CreateIndex( + name: "IX_CopyInfoBaseSteps_SourceInfoBaseId", + table: "CopyInfoBaseSteps", + column: "SourceInfoBaseId"); + + migrationBuilder.CreateIndex( + name: "IX_CustomNotifications_NotificationRecipientId", + table: "CustomNotifications", + column: "NotificationRecipientId"); + migrationBuilder.CreateIndex( name: "IX_EventLogSettings_CredentialsId", table: "EventLogSettings", @@ -552,6 +820,11 @@ namespace OneSwiss.Server.Migrations table: "EventLogSettings", column: "DbmsId"); + migrationBuilder.CreateIndex( + name: "IX_ExecuteOneScriptSteps_FileId", + table: "ExecuteOneScriptSteps", + column: "FileId"); + migrationBuilder.CreateIndex( name: "IX_InfoBaseMaintenanceTask_MaintenanceTaskId", table: "InfoBaseMaintenanceTask", @@ -567,26 +840,76 @@ namespace OneSwiss.Server.Migrations table: "InfoBases", column: "CredentialsId"); + migrationBuilder.CreateIndex( + name: "IX_LoadConfigurationSteps_ConfigurationRepositoryId", + table: "LoadConfigurationSteps", + column: "ConfigurationRepositoryId"); + + migrationBuilder.CreateIndex( + name: "IX_LoadConfigurationSteps_FileId", + table: "LoadConfigurationSteps", + column: "FileId"); + + migrationBuilder.CreateIndex( + name: "IX_LoadExtensionSteps_ConfigurationRepositoryId", + table: "LoadExtensionSteps", + column: "ConfigurationRepositoryId"); + + migrationBuilder.CreateIndex( + name: "IX_LoadExtensionSteps_FileId", + table: "LoadExtensionSteps", + column: "FileId"); + migrationBuilder.CreateIndex( name: "IX_LogTemplateTechLogSeance_TemplatesId", table: "LogTemplateTechLogSeance", column: "TemplatesId"); migrationBuilder.CreateIndex( - name: "IX_MaintenanceSteps_ConfigurationRepositoryId", + name: "IX_MaintenanceSteps_CopyInfoBaseStepId", table: "MaintenanceSteps", - column: "ConfigurationRepositoryId"); + column: "CopyInfoBaseStepId"); migrationBuilder.CreateIndex( - name: "IX_MaintenanceSteps_FileId", + name: "IX_MaintenanceSteps_DeleteExtensionStepId", table: "MaintenanceSteps", - column: "FileId"); + column: "DeleteExtensionStepId"); + + migrationBuilder.CreateIndex( + name: "IX_MaintenanceSteps_ExecuteOneScriptStepId", + table: "MaintenanceSteps", + column: "ExecuteOneScriptStepId"); + + migrationBuilder.CreateIndex( + name: "IX_MaintenanceSteps_LoadConfigurationStepId", + table: "MaintenanceSteps", + column: "LoadConfigurationStepId"); + + migrationBuilder.CreateIndex( + name: "IX_MaintenanceSteps_LoadExtensionStepId", + table: "MaintenanceSteps", + column: "LoadExtensionStepId"); + + migrationBuilder.CreateIndex( + name: "IX_MaintenanceSteps_LockConnectionsStepId", + table: "MaintenanceSteps", + column: "LockConnectionsStepId"); migrationBuilder.CreateIndex( name: "IX_MaintenanceSteps_MaintenanceTaskId", table: "MaintenanceSteps", column: "MaintenanceTaskId"); + migrationBuilder.CreateIndex( + name: "IX_MaintenanceSteps_StartExternalDataProcessorStepId", + table: "MaintenanceSteps", + column: "StartExternalDataProcessorStepId"); + + migrationBuilder.CreateIndex( + name: "IX_MaintenanceSteps_UpdateConfigurationStepId", + table: "MaintenanceSteps", + column: "UpdateConfigurationStepId"); + migrationBuilder.CreateIndex( name: "IX_MaintenanceTaskLogs_InfoBaseId", table: "MaintenanceTaskLogs", @@ -602,6 +925,11 @@ namespace OneSwiss.Server.Migrations table: "MaintenanceTaskLogs", column: "TaskId"); + migrationBuilder.CreateIndex( + name: "IX_StartExternalDataProcessorSteps_FileId", + table: "StartExternalDataProcessorSteps", + column: "FileId"); + migrationBuilder.CreateIndex( name: "IX_TechLogSettings_CredentialsId", table: "TechLogSettings", @@ -611,17 +939,28 @@ namespace OneSwiss.Server.Migrations name: "IX_TechLogSettings_DbmsId", table: "TechLogSettings", column: "DbmsId"); + + migrationBuilder.CreateIndex( + name: "IX_UpdateConfigurationSteps_FileId", + table: "UpdateConfigurationSteps", + column: "FileId"); } /// protected override void Down(MigrationBuilder migrationBuilder) { + migrationBuilder.DropTable( + name: "AgentMaintenanceTask"); + migrationBuilder.DropTable( name: "AgentTechLogSeance"); migrationBuilder.DropTable( name: "ConfigRepositoryUsers"); + migrationBuilder.DropTable( + name: "CustomNotifications"); + migrationBuilder.DropTable( name: "ErrorLoggingServiceSettings"); @@ -640,9 +979,6 @@ namespace OneSwiss.Server.Migrations migrationBuilder.DropTable( name: "MaintenanceTaskLogs"); - migrationBuilder.DropTable( - name: "NotificationRecipients"); - migrationBuilder.DropTable( name: "Notifications"); @@ -655,15 +991,15 @@ namespace OneSwiss.Server.Migrations migrationBuilder.DropTable( name: "TelegramBotSettings"); + migrationBuilder.DropTable( + name: "NotificationRecipients"); + migrationBuilder.DropTable( name: "LogTemplates"); migrationBuilder.DropTable( name: "TechLogSeances"); - migrationBuilder.DropTable( - name: "InfoBases"); - migrationBuilder.DropTable( name: "MaintenanceSteps"); @@ -671,7 +1007,34 @@ namespace OneSwiss.Server.Migrations name: "Dbms"); migrationBuilder.DropTable( - name: "Clusters"); + name: "CopyInfoBaseSteps"); + + migrationBuilder.DropTable( + name: "DeleteExtensionSteps"); + + migrationBuilder.DropTable( + name: "ExecuteOneScriptSteps"); + + migrationBuilder.DropTable( + name: "LoadConfigurationSteps"); + + migrationBuilder.DropTable( + name: "LoadExtensionSteps"); + + migrationBuilder.DropTable( + name: "LockConnectionsSteps"); + + migrationBuilder.DropTable( + name: "MaintenanceTasks"); + + migrationBuilder.DropTable( + name: "StartExternalDataProcessorSteps"); + + migrationBuilder.DropTable( + name: "UpdateConfigurationSteps"); + + migrationBuilder.DropTable( + name: "InfoBases"); migrationBuilder.DropTable( name: "ConfigRepositories"); @@ -680,7 +1043,7 @@ namespace OneSwiss.Server.Migrations name: "Files"); migrationBuilder.DropTable( - name: "MaintenanceTasks"); + name: "Clusters"); migrationBuilder.DropTable( name: "Agents"); diff --git a/oneswiss-server/Migrations/AppDbContextModelSnapshot.cs b/oneswiss-server/Migrations/AppDbContextModelSnapshot.cs index c9cde70..2a6f9d4 100644 --- a/oneswiss-server/Migrations/AppDbContextModelSnapshot.cs +++ b/oneswiss-server/Migrations/AppDbContextModelSnapshot.cs @@ -16,6 +16,21 @@ namespace OneSwiss.Server.Migrations #pragma warning disable 612, 618 modelBuilder.HasAnnotation("ProductVersion", "9.0.7"); + modelBuilder.Entity("AgentMaintenanceTask", b => + { + b.Property("AgentsId") + .HasColumnType("TEXT"); + + b.Property("MaintenanceTasksId") + .HasColumnType("TEXT"); + + b.HasKey("AgentsId", "MaintenanceTasksId"); + + b.HasIndex("MaintenanceTasksId"); + + b.ToTable("AgentMaintenanceTask"); + }); + modelBuilder.Entity("AgentTechLogSeance", b => { b.Property("AgentsId") @@ -424,6 +439,76 @@ namespace OneSwiss.Server.Migrations b.ToTable("LogTemplates"); }); + modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.CopyInfoBaseStep", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("DestinationCredentialsId") + .HasColumnType("TEXT"); + + b.Property("DestinationInfoBaseId") + .HasColumnType("TEXT"); + + b.Property("SourceCredentialsId") + .HasColumnType("TEXT"); + + b.Property("SourceInfoBaseId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("DestinationCredentialsId"); + + b.HasIndex("DestinationInfoBaseId"); + + b.HasIndex("SourceCredentialsId"); + + b.HasIndex("SourceInfoBaseId"); + + b.ToTable("CopyInfoBaseSteps"); + }); + + modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.DeleteExtensionStep", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ExtensionName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("DeleteExtensionSteps"); + }); + + modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.ExecuteOneScriptStep", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("DebugMode") + .HasColumnType("INTEGER"); + + b.Property("ExecutablePath") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("FileId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("FileId"); + + b.ToTable("ExecuteOneScriptSteps"); + }); + modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.InfoBaseMaintenanceTask", b => { b.Property("InfoBaseId") @@ -439,20 +524,34 @@ namespace OneSwiss.Server.Migrations b.ToTable("InfoBaseMaintenanceTask"); }); - modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.MaintenanceStep", b => + modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.LoadConfigurationStep", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("TEXT"); - b.Property("AccessCode") - .IsRequired() - .HasMaxLength(20) + b.Property("ConfigurationRepositoryId") .HasColumnType("TEXT"); - b.Property("CommandLineArguments") - .IsRequired() - .HasMaxLength(1000) + b.Property("FileId") + .HasColumnType("TEXT"); + + b.Property("FromConfigRepository") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ConfigurationRepositoryId"); + + b.HasIndex("FileId"); + + b.ToTable("LoadConfigurationSteps"); + }); + + modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.LoadExtensionStep", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() .HasColumnType("TEXT"); b.Property("ConfigurationRepositoryId") @@ -469,19 +568,68 @@ namespace OneSwiss.Server.Migrations b.Property("FromConfigRepository") .HasColumnType("INTEGER"); + b.HasKey("Id"); + + b.HasIndex("ConfigurationRepositoryId"); + + b.HasIndex("FileId"); + + b.ToTable("LoadExtensionSteps"); + }); + + modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.LockConnectionsStep", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AccessCode") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("TEXT"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("LockConnectionsSteps"); + }); + + modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.MaintenanceStep", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CopyInfoBaseStepId") + .HasColumnType("TEXT"); + + b.Property("DeleteExtensionStepId") + .HasColumnType("TEXT"); + + b.Property("ExecuteOneScriptStepId") + .HasColumnType("TEXT"); + b.Property("Kind") .HasColumnType("INTEGER"); b.Property("LeftStepId") .HasColumnType("TEXT"); - b.Property("MaintenanceTaskId") - .IsRequired() + b.Property("LoadConfigurationStepId") .HasColumnType("TEXT"); - b.Property("Message") + b.Property("LoadExtensionStepId") + .HasColumnType("TEXT"); + + b.Property("LockConnectionsStepId") + .HasColumnType("TEXT"); + + b.Property("MaintenanceTaskId") .IsRequired() - .HasMaxLength(200) .HasColumnType("TEXT"); b.Property("NodeKind") @@ -499,14 +647,32 @@ namespace OneSwiss.Server.Migrations b.Property("RightStepId") .HasColumnType("TEXT"); + b.Property("StartExternalDataProcessorStepId") + .HasColumnType("TEXT"); + + b.Property("UpdateConfigurationStepId") + .HasColumnType("TEXT"); + b.HasKey("Id"); - b.HasIndex("ConfigurationRepositoryId"); + b.HasIndex("CopyInfoBaseStepId"); - b.HasIndex("FileId"); + b.HasIndex("DeleteExtensionStepId"); + + b.HasIndex("ExecuteOneScriptStepId"); + + b.HasIndex("LoadConfigurationStepId"); + + b.HasIndex("LoadExtensionStepId"); + + b.HasIndex("LockConnectionsStepId"); b.HasIndex("MaintenanceTaskId"); + b.HasIndex("StartExternalDataProcessorStepId"); + + b.HasIndex("UpdateConfigurationStepId"); + b.ToTable("MaintenanceSteps"); }); @@ -516,6 +682,9 @@ namespace OneSwiss.Server.Migrations .ValueGeneratedOnAdd() .HasColumnType("TEXT"); + b.Property("CommonDestination") + .HasColumnType("INTEGER"); + b.Property("Description") .IsRequired() .HasMaxLength(200) @@ -578,6 +747,38 @@ namespace OneSwiss.Server.Migrations b.ToTable("MaintenanceTaskLogs"); }); + modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.StartExternalDataProcessorStep", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("FileId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("FileId"); + + b.ToTable("StartExternalDataProcessorSteps"); + }); + + modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.UpdateConfigurationStep", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("FileId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("FileId"); + + b.ToTable("UpdateConfigurationSteps"); + }); + modelBuilder.Entity("OneSwiss.Server.Models.Notification", b => { b.Property("Id") @@ -731,6 +932,21 @@ namespace OneSwiss.Server.Migrations b.ToTable("TelegramBotSettings"); }); + modelBuilder.Entity("AgentMaintenanceTask", b => + { + b.HasOne("OneSwiss.Server.Models.Agent", null) + .WithMany() + .HasForeignKey("AgentsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("OneSwiss.Server.Models.MaintenanceTasks.MaintenanceTask", null) + .WithMany() + .HasForeignKey("MaintenanceTasksId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + modelBuilder.Entity("AgentTechLogSeance", b => { b.HasOne("OneSwiss.Server.Models.Agent", null) @@ -848,6 +1064,42 @@ namespace OneSwiss.Server.Migrations b.Navigation("Credentials"); }); + modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.CopyInfoBaseStep", b => + { + b.HasOne("OneSwiss.Server.Models.Credentials", "DestinationCredentials") + .WithMany() + .HasForeignKey("DestinationCredentialsId"); + + b.HasOne("OneSwiss.Server.Models.InfoBase", "DestinationInfoBase") + .WithMany() + .HasForeignKey("DestinationInfoBaseId"); + + b.HasOne("OneSwiss.Server.Models.Credentials", "SourceCredentials") + .WithMany() + .HasForeignKey("SourceCredentialsId"); + + b.HasOne("OneSwiss.Server.Models.InfoBase", "SourceInfoBase") + .WithMany() + .HasForeignKey("SourceInfoBaseId"); + + b.Navigation("DestinationCredentials"); + + b.Navigation("DestinationInfoBase"); + + b.Navigation("SourceCredentials"); + + b.Navigation("SourceInfoBase"); + }); + + modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.ExecuteOneScriptStep", b => + { + b.HasOne("OneSwiss.Server.Models.File", "File") + .WithMany() + .HasForeignKey("FileId"); + + b.Navigation("File"); + }); + modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.InfoBaseMaintenanceTask", b => { b.HasOne("OneSwiss.Server.Models.InfoBase", "InfoBase") @@ -867,7 +1119,7 @@ namespace OneSwiss.Server.Migrations b.Navigation("MaintenanceTask"); }); - modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.MaintenanceStep", b => + modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.LoadConfigurationStep", b => { b.HasOne("OneSwiss.Server.Models.ConfigurationRepository", "ConfigurationRepository") .WithMany() @@ -877,17 +1129,83 @@ namespace OneSwiss.Server.Migrations .WithMany() .HasForeignKey("FileId"); + b.Navigation("ConfigurationRepository"); + + b.Navigation("File"); + }); + + modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.LoadExtensionStep", b => + { + b.HasOne("OneSwiss.Server.Models.ConfigurationRepository", "ConfigurationRepository") + .WithMany() + .HasForeignKey("ConfigurationRepositoryId"); + + b.HasOne("OneSwiss.Server.Models.File", "File") + .WithMany() + .HasForeignKey("FileId"); + + b.Navigation("ConfigurationRepository"); + + b.Navigation("File"); + }); + + modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.MaintenanceStep", b => + { + b.HasOne("OneSwiss.Server.Models.MaintenanceTasks.CopyInfoBaseStep", "CopyInfoBaseStep") + .WithMany() + .HasForeignKey("CopyInfoBaseStepId"); + + b.HasOne("OneSwiss.Server.Models.MaintenanceTasks.DeleteExtensionStep", "DeleteExtensionStep") + .WithMany() + .HasForeignKey("DeleteExtensionStepId"); + + b.HasOne("OneSwiss.Server.Models.MaintenanceTasks.ExecuteOneScriptStep", "ExecuteOneScriptStep") + .WithMany() + .HasForeignKey("ExecuteOneScriptStepId"); + + b.HasOne("OneSwiss.Server.Models.MaintenanceTasks.LoadConfigurationStep", "LoadConfigurationStep") + .WithMany() + .HasForeignKey("LoadConfigurationStepId"); + + b.HasOne("OneSwiss.Server.Models.MaintenanceTasks.LoadExtensionStep", "LoadExtensionStep") + .WithMany() + .HasForeignKey("LoadExtensionStepId"); + + b.HasOne("OneSwiss.Server.Models.MaintenanceTasks.LockConnectionsStep", "LockConnectionsStep") + .WithMany() + .HasForeignKey("LockConnectionsStepId"); + b.HasOne("OneSwiss.Server.Models.MaintenanceTasks.MaintenanceTask", "MaintenanceTask") .WithMany("Steps") .HasForeignKey("MaintenanceTaskId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.Navigation("ConfigurationRepository"); + b.HasOne("OneSwiss.Server.Models.MaintenanceTasks.StartExternalDataProcessorStep", "StartExternalDataProcessorStep") + .WithMany() + .HasForeignKey("StartExternalDataProcessorStepId"); - b.Navigation("File"); + b.HasOne("OneSwiss.Server.Models.MaintenanceTasks.UpdateConfigurationStep", "UpdateConfigurationStep") + .WithMany() + .HasForeignKey("UpdateConfigurationStepId"); + + b.Navigation("CopyInfoBaseStep"); + + b.Navigation("DeleteExtensionStep"); + + b.Navigation("ExecuteOneScriptStep"); + + b.Navigation("LoadConfigurationStep"); + + b.Navigation("LoadExtensionStep"); + + b.Navigation("LockConnectionsStep"); b.Navigation("MaintenanceTask"); + + b.Navigation("StartExternalDataProcessorStep"); + + b.Navigation("UpdateConfigurationStep"); }); modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.MaintenanceTaskLogItem", b => @@ -914,6 +1232,24 @@ namespace OneSwiss.Server.Migrations b.Navigation("Task"); }); + modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.StartExternalDataProcessorStep", b => + { + b.HasOne("OneSwiss.Server.Models.File", "File") + .WithMany() + .HasForeignKey("FileId"); + + b.Navigation("File"); + }); + + modelBuilder.Entity("OneSwiss.Server.Models.MaintenanceTasks.UpdateConfigurationStep", b => + { + b.HasOne("OneSwiss.Server.Models.File", "File") + .WithMany() + .HasForeignKey("FileId"); + + b.Navigation("File"); + }); + modelBuilder.Entity("OneSwiss.Server.Models.TechLogSettings", b => { b.HasOne("OneSwiss.Server.Models.Credentials", "Credentials") diff --git a/oneswiss-server/Models/Agent.cs b/oneswiss-server/Models/Agent.cs index 492b1de..9b4cabf 100644 --- a/oneswiss-server/Models/Agent.cs +++ b/oneswiss-server/Models/Agent.cs @@ -1,4 +1,6 @@ using System.ComponentModel.DataAnnotations; +using Microsoft.EntityFrameworkCore; +using OneSwiss.Server.Models.MaintenanceTasks; namespace OneSwiss.Server.Models { @@ -9,6 +11,8 @@ namespace OneSwiss.Server.Models public virtual List Clusters { get; set; } = []; public virtual List TechLogSeances { get; set; } = []; + [DeleteBehavior(DeleteBehavior.SetNull)] + public virtual List MaintenanceTasks { get; set; } = []; public override bool Equals(object? obj) => obj is Agent agent && diff --git a/oneswiss-server/Models/DatabaseObject.cs b/oneswiss-server/Models/DatabaseObject.cs index a360a37..520a0b5 100644 --- a/oneswiss-server/Models/DatabaseObject.cs +++ b/oneswiss-server/Models/DatabaseObject.cs @@ -1,3 +1,7 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; + namespace OneSwiss.Server.Models; public abstract class DatabaseObject : IHasId diff --git a/oneswiss-server/Models/MaintenanceTasks/CopyInfoBaseStep.cs b/oneswiss-server/Models/MaintenanceTasks/CopyInfoBaseStep.cs new file mode 100644 index 0000000..4ded2e0 --- /dev/null +++ b/oneswiss-server/Models/MaintenanceTasks/CopyInfoBaseStep.cs @@ -0,0 +1,22 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace OneSwiss.Server.Models.MaintenanceTasks; + +public class CopyInfoBaseStep : DatabaseObject +{ + public Guid? SourceCredentialsId { get; set; } + [ForeignKey(nameof(SourceCredentialsId))] + public Credentials? SourceCredentials { get; set; } + + public Guid? SourceInfoBaseId { get; set; } + [ForeignKey(nameof(SourceInfoBaseId))] + public InfoBase? SourceInfoBase { get; set; } + + public Guid? DestinationCredentialsId { get; set; } + [ForeignKey(nameof(DestinationCredentialsId))] + public Credentials? DestinationCredentials { get; set; } + + public Guid? DestinationInfoBaseId { get; set; } + [ForeignKey(nameof(DestinationInfoBaseId))] + public InfoBase? DestinationInfoBase { get; set; } +} \ No newline at end of file diff --git a/oneswiss-server/Models/MaintenanceTasks/DeleteExtensionStep.cs b/oneswiss-server/Models/MaintenanceTasks/DeleteExtensionStep.cs new file mode 100644 index 0000000..d299483 --- /dev/null +++ b/oneswiss-server/Models/MaintenanceTasks/DeleteExtensionStep.cs @@ -0,0 +1,9 @@ +using System.ComponentModel.DataAnnotations; + +namespace OneSwiss.Server.Models.MaintenanceTasks; + +public class DeleteExtensionStep : DatabaseObject +{ + [MaxLength(200)] + public string ExtensionName { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/oneswiss-server/Models/MaintenanceTasks/ExecuteOneScriptStep.cs b/oneswiss-server/Models/MaintenanceTasks/ExecuteOneScriptStep.cs new file mode 100644 index 0000000..c8ffbb1 --- /dev/null +++ b/oneswiss-server/Models/MaintenanceTasks/ExecuteOneScriptStep.cs @@ -0,0 +1,12 @@ +using System.ComponentModel.DataAnnotations; + +namespace OneSwiss.Server.Models.MaintenanceTasks; + +public class ExecuteOneScriptStep : DatabaseObject +{ + public bool DebugMode { get; set; } + [Required(AllowEmptyStrings = true)] + public string ExecutablePath { get; set; } = string.Empty; + public Guid? FileId { get; set; } + public File? File { get; set; } +} \ No newline at end of file diff --git a/oneswiss-server/Models/MaintenanceTasks/LoadConfigurationStep.cs b/oneswiss-server/Models/MaintenanceTasks/LoadConfigurationStep.cs new file mode 100644 index 0000000..5b6955b --- /dev/null +++ b/oneswiss-server/Models/MaintenanceTasks/LoadConfigurationStep.cs @@ -0,0 +1,15 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace OneSwiss.Server.Models.MaintenanceTasks; + +public class LoadConfigurationStep : DatabaseObject +{ + public bool FromConfigRepository { get; set; } + public Guid? FileId { get; set; } + public Guid? ConfigurationRepositoryId { get; set; } + + [ForeignKey(nameof(FileId))] + public File? File { get; set; } + [ForeignKey(nameof(ConfigurationRepositoryId))] + public ConfigurationRepository? ConfigurationRepository { get; set; } +} \ No newline at end of file diff --git a/oneswiss-server/Models/MaintenanceTasks/LoadExtensionStep.cs b/oneswiss-server/Models/MaintenanceTasks/LoadExtensionStep.cs new file mode 100644 index 0000000..de5af5d --- /dev/null +++ b/oneswiss-server/Models/MaintenanceTasks/LoadExtensionStep.cs @@ -0,0 +1,18 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace OneSwiss.Server.Models.MaintenanceTasks; + +public class LoadExtensionStep : DatabaseObject +{ + public bool FromConfigRepository { get; set; } + [MaxLength(200)] + public string ExtensionName { get; set; } = string.Empty; + public Guid? FileId { get; set; } + public Guid? ConfigurationRepositoryId { get; set; } + + [ForeignKey(nameof(FileId))] + public File? File { get; set; } + [ForeignKey(nameof(ConfigurationRepositoryId))] + public ConfigurationRepository? ConfigurationRepository { get; set; } +} \ No newline at end of file diff --git a/oneswiss-server/Models/MaintenanceTasks/LockConnectionsStep.cs b/oneswiss-server/Models/MaintenanceTasks/LockConnectionsStep.cs new file mode 100644 index 0000000..887195f --- /dev/null +++ b/oneswiss-server/Models/MaintenanceTasks/LockConnectionsStep.cs @@ -0,0 +1,12 @@ +using System.ComponentModel.DataAnnotations; +using MudBlazor; + +namespace OneSwiss.Server.Models.MaintenanceTasks; + +public class LockConnectionsStep : DatabaseObject +{ + [MaxLength(20)] + public string AccessCode { get; set; } = string.Empty; + [MaxLength(200)] + public string Message { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/oneswiss-server/Models/MaintenanceTasks/MaintenanceStep.cs b/oneswiss-server/Models/MaintenanceTasks/MaintenanceStep.cs index 17dcc9a..c8acac7 100644 --- a/oneswiss-server/Models/MaintenanceTasks/MaintenanceStep.cs +++ b/oneswiss-server/Models/MaintenanceTasks/MaintenanceStep.cs @@ -8,96 +8,52 @@ namespace OneSwiss.Server.Models.MaintenanceTasks; public class MaintenanceStep : DatabaseObject { - [Label("Задача")] public Guid MaintenanceTaskId { get; set; } - - [Label("Тип шага")] public MaintenanceStepKind Kind { get; set; } - [Label("Тип ноды")] public MaintenanceStepNodeKind NodeKind { get; set; } - - [Label("Предыдущий шаг")] public Guid? PreviousStepId { get; set; } - [Label("Левый шаг")] public Guid? LeftStepId { get; set; } - [Label("Правый шаг")] public Guid? RightStepId { get; set; } + public Guid? LockConnectionsStepId { get; set; } + public Guid? LoadConfigurationStepId { get; set; } + public Guid? DeleteExtensionStepId { get; set; } + public Guid? LoadExtensionStepId { get; set; } + public Guid? UpdateConfigurationStepId { get; set; } + public Guid? StartExternalDataProcessorStepId { get; set; } + public Guid? ExecuteOneScriptStepId { get; set; } + public Guid? CopyInfoBaseStepId { get; set; } - [Label("Код доступа")] - [MaxLength(20)] - public string AccessCode { get; set; } = string.Empty; - [Label("Сообщение")] - [MaxLength(200)] - public string Message { get; set; } = string.Empty; - [Label("Аргументы командной строки")] - [MaxLength(1000)] - public string CommandLineArguments { get; set; } = string.Empty; - [Label("Из хранилища")] - public bool FromConfigRepository { get; set; } - [Label("Хранилище")] - public Guid? ConfigurationRepositoryId { get; set; } - - [ForeignKey(nameof(ConfigurationRepositoryId))] - public ConfigurationRepository? ConfigurationRepository { get; set; } - [Label("Файл")] - public Guid? FileId { get; set; } - - [ForeignKey(nameof(FileId))] - public File? File { get; set; } - - [Label("Имя расширения")] - [MaxLength(200)] - public string ExtensionName { get; set; } = string.Empty; - - // Вспомогательные свойства для UI + // Вспомогательные свойства для UI ++ public double PositionX { get; set; } public double PositionY { get; set; } + // Вспомогательные свойства для UI -- [ForeignKey(nameof(MaintenanceTaskId))] public MaintenanceTask MaintenanceTask { get; set; } = null!; + + [ForeignKey(nameof(CopyInfoBaseStepId))] + public CopyInfoBaseStep? CopyInfoBaseStep { get; set; } + + [ForeignKey(nameof(ExecuteOneScriptStepId))] + public ExecuteOneScriptStep? ExecuteOneScriptStep { get; set; } + + [ForeignKey(nameof(StartExternalDataProcessorStepId))] + public StartExternalDataProcessorStep? StartExternalDataProcessorStep { get; set; } + + [ForeignKey(nameof(UpdateConfigurationStepId))] + public UpdateConfigurationStep? UpdateConfigurationStep { get; set; } + + [ForeignKey(nameof(LoadExtensionStepId))] + public LoadExtensionStep? LoadExtensionStep { get; set; } + + [ForeignKey(nameof(DeleteExtensionStepId))] + public DeleteExtensionStep? DeleteExtensionStep { get; set; } + + [ForeignKey(nameof(LoadConfigurationStepId))] + public LoadConfigurationStep? LoadConfigurationStep { get; set; } + + [ForeignKey(nameof(LockConnectionsStepId))] + public LockConnectionsStep? LockConnectionsStep { get; set; } + public virtual List Logs { get; set; } = []; - - public bool IsConfigurationUpdating() - => Kind is MaintenanceStepKind.LoadExtension - or MaintenanceStepKind.LoadConfiguration; - - public bool NeedSpecifyConfigRepository() - { - return Kind is MaintenanceStepKind.LoadExtension - or MaintenanceStepKind.LoadConfiguration && FromConfigRepository; - } - - public bool NeedLoadFiles() - { - if (Kind is MaintenanceStepKind.LoadExtension - or MaintenanceStepKind.LoadConfiguration) - return !FromConfigRepository; - - return Kind is MaintenanceStepKind.ExecuteOneScript - or MaintenanceStepKind.StartExternalDataProcessor - or MaintenanceStepKind.UpdateConfiguration; - } - - public bool NeeSpecifyAccessCode() - => Kind is MaintenanceStepKind.LockConnections; - - public bool NeeSpecifyMessage() - => Kind is MaintenanceStepKind.LockConnections; - - public bool NeeSpecifyExtensionName() - => Kind is MaintenanceStepKind.LoadExtension - or MaintenanceStepKind.DeleteExtension; - - [JsonIgnore] - public FileType AvailableFileType - // ReSharper disable once SwitchExpressionHandlesSomeKnownEnumValuesWithExceptionInDefault - => Kind switch - { - MaintenanceStepKind.LoadExtension => FileType.Cfe, - MaintenanceStepKind.UpdateConfiguration => FileType.Cfu, - MaintenanceStepKind.LoadConfiguration => FileType.Cf, - MaintenanceStepKind.StartExternalDataProcessor => FileType.Epf, - MaintenanceStepKind.ExecuteOneScript => FileType.Ospx, - _ => throw new ArgumentOutOfRangeException() - }; } \ No newline at end of file diff --git a/oneswiss-server/Models/MaintenanceTasks/MaintenanceTask.cs b/oneswiss-server/Models/MaintenanceTasks/MaintenanceTask.cs index 27c3c30..66a57c6 100644 --- a/oneswiss-server/Models/MaintenanceTasks/MaintenanceTask.cs +++ b/oneswiss-server/Models/MaintenanceTasks/MaintenanceTask.cs @@ -17,8 +17,11 @@ public class MaintenanceTask : DatabaseObject public DateTime FinishDateTime { get; set; } = DateTime.MinValue; [Label("Это шаблон")] public bool IsTemplate { get; set; } = false; + [Label("Это задача общего назначения")] + public bool CommonDestination { get; set; } = false; public virtual List Steps { get; set; } = []; + public virtual List Agents { get; set; } = []; public virtual List InfoBases { get; set; } = []; public virtual List Logs { get; set; } = []; } \ No newline at end of file diff --git a/oneswiss-server/Models/MaintenanceTasks/StartExternalDataProcessorStep.cs b/oneswiss-server/Models/MaintenanceTasks/StartExternalDataProcessorStep.cs new file mode 100644 index 0000000..2e08465 --- /dev/null +++ b/oneswiss-server/Models/MaintenanceTasks/StartExternalDataProcessorStep.cs @@ -0,0 +1,7 @@ +namespace OneSwiss.Server.Models.MaintenanceTasks; + +public class StartExternalDataProcessorStep : DatabaseObject +{ + public Guid? FileId { get; set; } + public File? File { get; set; } +} \ No newline at end of file diff --git a/oneswiss-server/Models/MaintenanceTasks/UpdateConfigurationStep.cs b/oneswiss-server/Models/MaintenanceTasks/UpdateConfigurationStep.cs new file mode 100644 index 0000000..66c8f2b --- /dev/null +++ b/oneswiss-server/Models/MaintenanceTasks/UpdateConfigurationStep.cs @@ -0,0 +1,11 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace OneSwiss.Server.Models.MaintenanceTasks; + +public class UpdateConfigurationStep : DatabaseObject +{ + public Guid? FileId { get; set; } + + [ForeignKey(nameof(FileId))] + public File? File { get; set; } +} \ No newline at end of file