mirror of
https://github.com/akpaevj/onecmonitor.git
synced 2025-03-03 14:42:18 +02:00
промежуточная
This commit is contained in:
parent
aae7d51c31
commit
da06b000c1
@ -1,7 +1,8 @@
|
||||
using System.Diagnostics;
|
||||
using OneSTools.Common.Extensions;
|
||||
using OneSTools.Common.Platform.Services;
|
||||
|
||||
namespace OneSTools.Common.Platform;
|
||||
namespace OneSTools.Common.Platform.RemoteAdministration;
|
||||
|
||||
public class Rac(V8Platform platform, string host = "localhost", int port = 1545)
|
||||
{
|
||||
@ -14,7 +15,7 @@ public class Rac(V8Platform platform, string host = "localhost", int port = 1545
|
||||
}
|
||||
|
||||
public List<V8Cluster> GetClusters()
|
||||
=> GetOutputItems("cluster list")
|
||||
=> GetOutputItems("cluster list", 10)
|
||||
.Select(c => new V8Cluster
|
||||
{
|
||||
Id = c["cluster"],
|
||||
@ -23,12 +24,9 @@ public class Rac(V8Platform platform, string host = "localhost", int port = 1545
|
||||
Port = int.Parse(c["port"])
|
||||
})
|
||||
.ToList();
|
||||
|
||||
public List<V8InfoBaseSummary> GetInfoBasesSummaries(V8Cluster cluster)
|
||||
=> GetInfoBasesSummaries(cluster.Id);
|
||||
|
||||
public List<V8InfoBaseSummary> GetInfoBasesSummaries(string clusterId)
|
||||
=> GetOutputItems($"infobase --cluster={clusterId} summary list")
|
||||
=> GetOutputItems($"infobase --cluster={clusterId} summary list", 10)
|
||||
.Select(c => new V8InfoBaseSummary()
|
||||
{
|
||||
Id = c["infobase"],
|
||||
@ -36,11 +34,8 @@ public class Rac(V8Platform platform, string host = "localhost", int port = 1545
|
||||
})
|
||||
.ToList();
|
||||
|
||||
public V8InfoBase GetInfoBase(V8Cluster cluster, V8InfoBaseSummary infoBaseSummary, string user, string password)
|
||||
=> GetInfoBase(cluster.Id, infoBaseSummary.Name, user, password);
|
||||
|
||||
public V8InfoBase GetInfoBase(string clusterId, string infoBaseId, string user, string password)
|
||||
=> GetOutputItems($"infobase --cluster={clusterId} info --infobase={infoBaseId} --infobase-user={user} --infobase-pwd={password}")
|
||||
=> GetOutputItems($"infobase --cluster={clusterId} info --infobase={infoBaseId} --infobase-user={user} --infobase-pwd={password}", 20)
|
||||
.Select(c => new V8InfoBase
|
||||
{
|
||||
Id = c["infobase"],
|
||||
@ -54,16 +49,13 @@ public class Rac(V8Platform platform, string host = "localhost", int port = 1545
|
||||
|
||||
public void BlockConnections(string clusterId, string infoBaseId, string user, string password,
|
||||
string permissionCode, string deniedMessage)
|
||||
=> StartRacAndGetOutput($"infobase --cluster={clusterId} update --infobase={infoBaseId} --infobase-user={user} --infobase-pwd={password} --sessions-deny=on --scheduled-jobs-deny=on --permission-code={permissionCode} --denied-message=\"{deniedMessage}\"");
|
||||
|
||||
public List<V8Session> GetInfoBaseSessions(V8Cluster cluster, V8InfoBase infoBase)
|
||||
=> GetInfoBaseSessions(cluster.Id, infoBase.Id);
|
||||
=> StartRacAndGetOutput($"infobase --cluster={clusterId} update --infobase={infoBaseId} --infobase-user={user} --infobase-pwd={password} --sessions-deny=on --scheduled-jobs-deny=on --permission-code={permissionCode} --denied-message=\"{deniedMessage}\"", 10);
|
||||
|
||||
public List<V8Session> GetInfoBaseSessions(string clusterId, string infoBaseId)
|
||||
{
|
||||
var infoBases = GetInfoBasesSummaries(clusterId);
|
||||
|
||||
return GetOutputItems($"session --cluster={clusterId} list --infobase={infoBaseId}")
|
||||
return GetOutputItems($"session --cluster={clusterId} list --infobase={infoBaseId}", 20)
|
||||
.Select(c => new V8Session
|
||||
{
|
||||
Id = c["session"],
|
||||
@ -76,21 +68,15 @@ public class Rac(V8Platform platform, string host = "localhost", int port = 1545
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public void TerminateSession(V8Cluster cluster, V8Session session)
|
||||
=> TerminateSession(cluster.Id, session.Id);
|
||||
|
||||
public void TerminateSession(string clusterId, string sessionId)
|
||||
=> StartRacAndGetOutput($"session --cluster={clusterId} terminate --session={sessionId}");
|
||||
|
||||
public void UnblockConnections(V8Cluster cluster, V8InfoBase infoBase, string user, string password)
|
||||
=> UnblockConnections(cluster.Id, infoBase.Id, user, password);
|
||||
=> StartRacAndGetOutput($"session --cluster={clusterId} terminate --session={sessionId}", 10);
|
||||
|
||||
public void UnblockConnections(string clusterId, string infoBaseId, string user, string password)
|
||||
=> StartRacAndGetOutput($"infobase --cluster={clusterId} update --infobase={infoBaseId} --infobase-user={user} --infobase-pwd={password} --sessions-deny=off --scheduled-jobs-deny=off");
|
||||
=> StartRacAndGetOutput($"infobase --cluster={clusterId} update --infobase={infoBaseId} --infobase-user={user} --infobase-pwd={password} --sessions-deny=off --scheduled-jobs-deny=off", 10);
|
||||
|
||||
private List<Dictionary<string, string>> GetOutputItems(string command)
|
||||
private List<Dictionary<string, string>> GetOutputItems(string command, int commandTimeout)
|
||||
{
|
||||
var output = StartRacAndGetOutput(command);
|
||||
var output = StartRacAndGetOutput(command, commandTimeout);
|
||||
|
||||
var outputItems = output.Split($"{Environment.NewLine}{Environment.NewLine}", StringSplitOptions.RemoveEmptyEntries).ToList();
|
||||
|
||||
@ -100,7 +86,7 @@ public class Rac(V8Platform platform, string host = "localhost", int port = 1545
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private string StartRacAndGetOutput(string command)
|
||||
private string StartRacAndGetOutput(string command, int commandTimeout)
|
||||
{
|
||||
if (!platform.HasRac)
|
||||
throw new Exception($"{platform.PlatformPath} doesn't contain 1cv8 executable");
|
||||
@ -111,20 +97,33 @@ public class Rac(V8Platform platform, string host = "localhost", int port = 1545
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardInput = true,
|
||||
RedirectStandardError = true,
|
||||
CreateNoWindow = true,
|
||||
UseShellExecute = false,
|
||||
Arguments = $"{host}:{port} {command}"
|
||||
};
|
||||
|
||||
using var process = new Process();
|
||||
process.StartInfo = psi;
|
||||
|
||||
if (!process.Start())
|
||||
throw new Exception($"Failed to start {psi.FileName}");
|
||||
|
||||
process.WaitForExit();
|
||||
if (!process.Start())
|
||||
{
|
||||
process.Close();
|
||||
throw new Exception($"Ошибка запуска {psi.FileName}");
|
||||
}
|
||||
|
||||
if (process.WaitForExit(TimeSpan.FromSeconds(commandTimeout)) && process.ExitCode != 0)
|
||||
{
|
||||
using var errorStream = process.StandardError;
|
||||
var error = errorStream.ReadToEnd();
|
||||
process.Close();
|
||||
|
||||
throw new Exception($"Ошибка выполнения команды RAC: {error}");
|
||||
}
|
||||
|
||||
if (process.ExitCode != 0)
|
||||
throw new Exception($"Failed to execute rac command {process.StandardError.ReadToEnd()}");
|
||||
using var outputStream = process.StandardOutput;
|
||||
var output = outputStream.ReadToEnd();
|
||||
process.Close();
|
||||
|
||||
return process.StandardOutput.ReadToEnd();
|
||||
return output;
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
using MessagePack;
|
||||
|
||||
namespace OneSTools.Common.Platform;
|
||||
namespace OneSTools.Common.Platform.RemoteAdministration;
|
||||
|
||||
[MessagePackObject]
|
||||
public class V8Cluster
|
@ -1,6 +1,6 @@
|
||||
using MessagePack;
|
||||
|
||||
namespace OneSTools.Common.Platform;
|
||||
namespace OneSTools.Common.Platform.RemoteAdministration;
|
||||
|
||||
[MessagePackObject]
|
||||
public class V8InfoBase : V8InfoBaseSummary
|
@ -1,6 +1,6 @@
|
||||
using MessagePack;
|
||||
|
||||
namespace OneSTools.Common.Platform;
|
||||
namespace OneSTools.Common.Platform.RemoteAdministration;
|
||||
|
||||
[MessagePackObject]
|
||||
public class V8InfoBaseSummary
|
@ -1,4 +1,4 @@
|
||||
namespace OneSTools.Common.Platform;
|
||||
namespace OneSTools.Common.Platform.RemoteAdministration;
|
||||
|
||||
public class V8Session
|
||||
{
|
@ -1,6 +1,6 @@
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace OneSTools.Common.Platform;
|
||||
namespace OneSTools.Common.Platform.Services;
|
||||
|
||||
public record ArgsKeyValue(string Key, string Value);
|
||||
|
@ -1,7 +1,8 @@
|
||||
using System.ComponentModel;
|
||||
using MessagePack;
|
||||
using OneSTools.Common.Platform.Services;
|
||||
|
||||
namespace OneSTools.Common.Platform;
|
||||
namespace OneSTools.Common.Platform.Services;
|
||||
|
||||
[DisplayName("Служба агента сервера 1С")]
|
||||
[MessagePackObject]
|
@ -1,7 +1,8 @@
|
||||
using System.ComponentModel;
|
||||
using MessagePack;
|
||||
using OneSTools.Common.Platform.Services;
|
||||
|
||||
namespace OneSTools.Common.Platform;
|
||||
namespace OneSTools.Common.Platform.Services;
|
||||
|
||||
[DisplayName("Служба сервера удаленного администрирования 1С")]
|
||||
[MessagePackObject]
|
@ -1,7 +1,7 @@
|
||||
using System.ComponentModel;
|
||||
using MessagePack;
|
||||
|
||||
namespace OneSTools.Common.Platform;
|
||||
namespace OneSTools.Common.Platform.Services;
|
||||
|
||||
[DisplayName("Служба 1С")]
|
||||
[MessagePackObject]
|
@ -1,4 +1,4 @@
|
||||
namespace OneSTools.Common.Platform;
|
||||
namespace OneSTools.Common.Platform.Services;
|
||||
|
||||
public enum V8ServiceType
|
||||
{
|
@ -4,7 +4,7 @@ using System.Text.RegularExpressions;
|
||||
using Microsoft.Win32;
|
||||
using OneSTools.Common.Extensions;
|
||||
|
||||
namespace OneSTools.Common.Platform;
|
||||
namespace OneSTools.Common.Platform.Services;
|
||||
|
||||
public static class V8Services
|
||||
{
|
55
OneSTools.Common/Platform/Unpack/BlockHeader.cs
Normal file
55
OneSTools.Common/Platform/Unpack/BlockHeader.cs
Normal file
@ -0,0 +1,55 @@
|
||||
namespace OneSTools.Common.Platform.Unpack;
|
||||
|
||||
public struct BlockHeader(
|
||||
uint dataSize = 0,
|
||||
uint pageSize = FileFormat.V8DefaultPageSize,
|
||||
uint nextPageAddr = FileFormat.V8FfSignature)
|
||||
{
|
||||
public uint DataSize { get; } = dataSize;
|
||||
public uint PageSize { get; } = pageSize;
|
||||
public uint NextPageAddr { get; } = nextPageAddr;
|
||||
|
||||
private static void ReadExpectedByte(Stream reader, int expectedValue)
|
||||
{
|
||||
if (reader.ReadByte() != expectedValue)
|
||||
throw new File8FormatException();
|
||||
}
|
||||
|
||||
private static uint ReadHexData(Stream reader)
|
||||
{
|
||||
var hex = new byte[8];
|
||||
|
||||
if (reader.Read(hex, 0, 8) < 8)
|
||||
{
|
||||
throw new File8FormatException();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return Convert.ToUInt32(System.Text.Encoding.ASCII.GetString(hex), 16);
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw new File8FormatException();
|
||||
}
|
||||
}
|
||||
|
||||
public static BlockHeader Read(Stream reader)
|
||||
{
|
||||
ReadExpectedByte(reader, 0x0D);
|
||||
ReadExpectedByte(reader, 0x0A);
|
||||
|
||||
var dataSize = ReadHexData(reader);
|
||||
ReadExpectedByte(reader, 0x20);
|
||||
var pageSize = ReadHexData(reader);
|
||||
ReadExpectedByte(reader, 0x20);
|
||||
var nextPageAddr = ReadHexData(reader);
|
||||
ReadExpectedByte(reader, 0x20);
|
||||
|
||||
ReadExpectedByte(reader, 0x0D);
|
||||
ReadExpectedByte(reader, 0x0A);
|
||||
|
||||
return new BlockHeader(dataSize, pageSize, nextPageAddr);
|
||||
}
|
||||
|
||||
}
|
152
OneSTools.Common/Platform/Unpack/BlockReader.cs
Normal file
152
OneSTools.Common/Platform/Unpack/BlockReader.cs
Normal file
@ -0,0 +1,152 @@
|
||||
using System.IO.Compression;
|
||||
|
||||
namespace OneSTools.Common.Platform.Unpack;
|
||||
|
||||
public class BlockReader : Stream
|
||||
{
|
||||
private BlockHeader _currentHeader;
|
||||
private readonly Stream _reader;
|
||||
private readonly int _dataSize;
|
||||
|
||||
private byte[] _currentPageData;
|
||||
private int _currentPageOffset;
|
||||
private bool _isPacked;
|
||||
private bool _isContainer;
|
||||
|
||||
public BlockReader(Stream basicStream)
|
||||
{
|
||||
_reader = basicStream;
|
||||
_currentHeader = BlockHeader.Read(_reader);
|
||||
_dataSize = (int)_currentHeader.DataSize;
|
||||
ReadPage();
|
||||
AnalyzeState();
|
||||
}
|
||||
|
||||
private void ReadPage()
|
||||
{
|
||||
var currentDataSize = Math.Min(_dataSize, (int)_currentHeader.PageSize);
|
||||
_currentPageData = new byte[currentDataSize];
|
||||
_reader.Read(_currentPageData, 0, currentDataSize);
|
||||
_currentPageOffset = 0;
|
||||
}
|
||||
|
||||
private void AnalyzeState()
|
||||
{
|
||||
var bufferToCheck = _currentPageData;
|
||||
|
||||
try
|
||||
{
|
||||
using var inputStream = new MemoryStream(bufferToCheck);
|
||||
using var deflateStream = new DeflateStream(inputStream, CompressionMode.Decompress);
|
||||
|
||||
using var outputStream = new MemoryStream();
|
||||
deflateStream.CopyTo(outputStream);
|
||||
|
||||
var tmp = outputStream.ToArray();
|
||||
_isPacked = true;
|
||||
bufferToCheck = tmp;
|
||||
}
|
||||
catch
|
||||
{
|
||||
_isPacked = false;
|
||||
}
|
||||
|
||||
_isContainer = FileFormat.IsContainer(bufferToCheck);
|
||||
}
|
||||
|
||||
private void MoveNextBlock()
|
||||
{
|
||||
if (_currentHeader.NextPageAddr == FileFormat.V8FfSignature)
|
||||
{
|
||||
_currentPageData = null;
|
||||
return;
|
||||
}
|
||||
_reader.Seek(_currentHeader.NextPageAddr, SeekOrigin.Begin);
|
||||
_currentHeader = BlockHeader.Read(_reader);
|
||||
ReadPage();
|
||||
}
|
||||
|
||||
public bool IsPacked => _isPacked;
|
||||
|
||||
public bool IsContainer => _isContainer;
|
||||
|
||||
public override bool CanRead => true;
|
||||
|
||||
public override bool CanSeek => false;
|
||||
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public override long Length => _dataSize;
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get => throw new NotSupportedException();
|
||||
|
||||
set => throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (_currentPageData == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
var bytesRead = 0;
|
||||
var countLeft = count;
|
||||
|
||||
while (countLeft > 0)
|
||||
{
|
||||
var leftInPage = _currentPageData.Length - _currentPageOffset;
|
||||
|
||||
if (leftInPage == 0)
|
||||
{
|
||||
MoveNextBlock();
|
||||
|
||||
if (_currentPageData == null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var readFromCurrentPage = Math.Min(leftInPage, countLeft);
|
||||
|
||||
Buffer.BlockCopy(_currentPageData, _currentPageOffset, buffer, offset, readFromCurrentPage);
|
||||
_currentPageOffset += readFromCurrentPage;
|
||||
offset += readFromCurrentPage;
|
||||
|
||||
bytesRead += readFromCurrentPage;
|
||||
countLeft -= readFromCurrentPage;
|
||||
}
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public static byte[] ReadDataBlock(Stream reader)
|
||||
{
|
||||
var blockReader = new BlockReader(reader);
|
||||
var buf = new byte[blockReader.Length];
|
||||
blockReader.ReadExactly(buf, 0, buf.Length);
|
||||
return buf;
|
||||
}
|
||||
}
|
34
OneSTools.Common/Platform/Unpack/ContainerHeader.cs
Normal file
34
OneSTools.Common/Platform/Unpack/ContainerHeader.cs
Normal file
@ -0,0 +1,34 @@
|
||||
namespace OneSTools.Common.Platform.Unpack;
|
||||
|
||||
public struct ContainerHeader
|
||||
{
|
||||
public readonly uint NextPageAddr;
|
||||
public readonly uint PageSize;
|
||||
public readonly uint StorageVer;
|
||||
public readonly uint Reserved;
|
||||
|
||||
private ContainerHeader(uint nextPageAddr = FileFormat.V8FfSignature, uint pageSize = FileFormat.V8DefaultPageSize, uint storageVer = 0, uint reserved = 0)
|
||||
{
|
||||
NextPageAddr = nextPageAddr;
|
||||
PageSize = pageSize;
|
||||
StorageVer = 0;
|
||||
Reserved = 0;
|
||||
}
|
||||
|
||||
public static ContainerHeader Read(Stream reader)
|
||||
{
|
||||
const int headerSize = 16;
|
||||
var buf = new byte[headerSize];
|
||||
if (reader.Read(buf, 0, headerSize) < headerSize)
|
||||
{
|
||||
throw new File8FormatException();
|
||||
}
|
||||
|
||||
return new ContainerHeader(
|
||||
nextPageAddr: BitConverter.ToUInt32(buf, 0),
|
||||
pageSize: BitConverter.ToUInt32(buf, 4),
|
||||
storageVer: BitConverter.ToUInt32(buf, 8),
|
||||
reserved: BitConverter.ToUInt32(buf, 12)
|
||||
);
|
||||
}
|
||||
}
|
28
OneSTools.Common/Platform/Unpack/ElementAddress.cs
Normal file
28
OneSTools.Common/Platform/Unpack/ElementAddress.cs
Normal file
@ -0,0 +1,28 @@
|
||||
namespace OneSTools.Common.Platform.Unpack;
|
||||
|
||||
public readonly struct ElementAddress(uint headerAddress, uint dataAddress, uint signature = FileFormat.V8FfSignature)
|
||||
{
|
||||
public uint HeaderAddress { get; } = headerAddress;
|
||||
public uint DataAddress { get; } = dataAddress;
|
||||
public uint Signature { get; } = signature;
|
||||
|
||||
public static IList<ElementAddress> Parse(byte[] buf)
|
||||
{
|
||||
const int elementSize = 4 + 4 + 4;
|
||||
var result = new List<ElementAddress>();
|
||||
|
||||
for (var offset = 0; offset + elementSize <= buf.Length; offset += elementSize)
|
||||
{
|
||||
var headerAddress = BitConverter.ToUInt32(buf, offset);
|
||||
var dataAddress = BitConverter.ToUInt32(buf, offset + 4);
|
||||
var signature = BitConverter.ToUInt32(buf, offset + 8);
|
||||
|
||||
result.Add(new ElementAddress(headerAddress, dataAddress, signature));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
=> $"{HeaderAddress:x8}:{DataAddress:x8}:{Signature:x8}";
|
||||
}
|
29
OneSTools.Common/Platform/Unpack/ElementHeader.cs
Normal file
29
OneSTools.Common/Platform/Unpack/ElementHeader.cs
Normal file
@ -0,0 +1,29 @@
|
||||
namespace OneSTools.Common.Platform.Unpack;
|
||||
|
||||
public struct ElementHeader(string name, DateTime creationDate, DateTime modificationDate)
|
||||
{
|
||||
public readonly DateTime CreationDate = creationDate;
|
||||
public readonly DateTime ModificationDate = modificationDate;
|
||||
public readonly string Name = name;
|
||||
|
||||
public static DateTime File8Date(ulong serializedDate)
|
||||
{
|
||||
return new DateTime((long) serializedDate * 1000);
|
||||
}
|
||||
|
||||
public static ElementHeader Parse(byte[] buf)
|
||||
{
|
||||
var serializedCreationDate = BitConverter.ToUInt64(buf, 0);
|
||||
var serializedModificationDate = BitConverter.ToUInt64(buf, 8);
|
||||
// 4 байта на Reserved
|
||||
var enc = new System.Text.UnicodeEncoding(bigEndian: false, byteOrderMark: false);
|
||||
|
||||
const int nameOffset = 8 + 8 + 4;
|
||||
var name = enc.GetString(buf, nameOffset, buf.Length - nameOffset - 4).TrimEnd('\0');
|
||||
|
||||
var creationDate = File8Date(serializedCreationDate);
|
||||
var modificationDate = File8Date(serializedModificationDate);
|
||||
|
||||
return new ElementHeader(name, creationDate, modificationDate);
|
||||
}
|
||||
}
|
18
OneSTools.Common/Platform/Unpack/File8.cs
Normal file
18
OneSTools.Common/Platform/Unpack/File8.cs
Normal file
@ -0,0 +1,18 @@
|
||||
namespace OneSTools.Common.Platform.Unpack;
|
||||
|
||||
public class File8
|
||||
{
|
||||
internal File8(ElementHeader header, uint dataOffset)
|
||||
{
|
||||
DataOffset = (int)dataOffset;
|
||||
|
||||
Name = header.Name;
|
||||
ModificationTime = header.ModificationDate;
|
||||
CreationTime = header.CreationDate;
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
public DateTime ModificationTime { get; }
|
||||
public DateTime CreationTime { get; }
|
||||
public int DataOffset { get; }
|
||||
}
|
45
OneSTools.Common/Platform/Unpack/File8Collection.cs
Normal file
45
OneSTools.Common/Platform/Unpack/File8Collection.cs
Normal file
@ -0,0 +1,45 @@
|
||||
using System.Collections;
|
||||
|
||||
namespace OneSTools.Common.Platform.Unpack;
|
||||
|
||||
public class File8Collection : IEnumerable<File8>
|
||||
{
|
||||
private readonly IReadOnlyList<File8?> _data;
|
||||
|
||||
public File8Collection(IEnumerable<File8?> data)
|
||||
{
|
||||
var fileList = new List<File8?>();
|
||||
fileList.AddRange(data);
|
||||
_data = fileList;
|
||||
}
|
||||
|
||||
public int Count()
|
||||
{
|
||||
return _data.Count;
|
||||
}
|
||||
|
||||
public File8? Get(int index)
|
||||
{
|
||||
return _data[index];
|
||||
}
|
||||
|
||||
public File8? Get(string name)
|
||||
{
|
||||
return _data.First(f => f != null && f.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase));
|
||||
}
|
||||
|
||||
public File8? Find(string name)
|
||||
{
|
||||
return _data.FirstOrDefault(f => f != null && f.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase));
|
||||
}
|
||||
|
||||
public IEnumerator<File8> GetEnumerator()
|
||||
{
|
||||
return _data.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
3
OneSTools.Common/Platform/Unpack/File8FormatException.cs
Normal file
3
OneSTools.Common/Platform/Unpack/File8FormatException.cs
Normal file
@ -0,0 +1,3 @@
|
||||
namespace OneSTools.Common.Platform.Unpack;
|
||||
|
||||
public class File8FormatException : Exception;
|
138
OneSTools.Common/Platform/Unpack/File8Reader.cs
Normal file
138
OneSTools.Common/Platform/Unpack/File8Reader.cs
Normal file
@ -0,0 +1,138 @@
|
||||
using System.IO.Compression;
|
||||
using System.Reflection.Metadata;
|
||||
|
||||
namespace OneSTools.Common.Platform.Unpack;
|
||||
|
||||
public class File8Reader : IDisposable
|
||||
{
|
||||
private readonly Stream _reader;
|
||||
private readonly bool _dataPacked;
|
||||
private int _storageVersion;
|
||||
|
||||
public decimal StorageVersion => _storageVersion;
|
||||
public File8Collection Elements { get; }
|
||||
|
||||
public File8Reader(string filePath, bool dataPacked = true)
|
||||
{
|
||||
const int magicSize = 100 * 1024;
|
||||
var fileStream = new FileStream(filePath, FileMode.Open);
|
||||
if (fileStream.Length >= magicSize)
|
||||
_reader = fileStream;
|
||||
else
|
||||
{
|
||||
var memoryStream = new MemoryStream();
|
||||
fileStream.CopyTo(memoryStream);
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
_reader = memoryStream;
|
||||
}
|
||||
|
||||
_dataPacked = dataPacked;
|
||||
var fileList = ReadFileList();
|
||||
Elements = new File8Collection(fileList);
|
||||
}
|
||||
|
||||
public File8Reader(Stream stream, bool dataPacked = true)
|
||||
{
|
||||
_reader = stream;
|
||||
_dataPacked = dataPacked;
|
||||
var fileList = ReadFileList();
|
||||
Elements = new File8Collection(fileList);
|
||||
}
|
||||
|
||||
private List<File8> ReadFileList()
|
||||
{
|
||||
var containerHeader = ContainerHeader.Read(_reader);
|
||||
_storageVersion = (int)containerHeader.StorageVer;
|
||||
var elemsAddrBuf = BlockReader.ReadDataBlock(_reader);
|
||||
var addresses = ElementAddress.Parse(elemsAddrBuf);
|
||||
|
||||
var fileList = new List<File8>();
|
||||
foreach (var address in addresses)
|
||||
{
|
||||
if (address.HeaderAddress == FileFormat.V8FfSignature || address.Signature != FileFormat.V8FfSignature)
|
||||
continue;
|
||||
|
||||
_reader.Seek(address.HeaderAddress, SeekOrigin.Begin);
|
||||
var buf = BlockReader.ReadDataBlock(_reader);
|
||||
|
||||
var fileHeader = ElementHeader.Parse(buf);
|
||||
fileList.Add(new File8(fileHeader, address.DataAddress));
|
||||
}
|
||||
|
||||
return fileList;
|
||||
}
|
||||
|
||||
public void Extract(File8 element, string destDir, bool recursiveUnpack = false)
|
||||
{
|
||||
|
||||
if (!Directory.Exists(destDir))
|
||||
{
|
||||
Directory.CreateDirectory(destDir);
|
||||
}
|
||||
|
||||
Stream fileExtractor;
|
||||
|
||||
if (element.DataOffset == FileFormat.V8FfSignature)
|
||||
{
|
||||
// Файл есть, но пуст
|
||||
fileExtractor = new MemoryStream();
|
||||
}
|
||||
else
|
||||
{
|
||||
_reader.Seek(element.DataOffset, SeekOrigin.Begin);
|
||||
|
||||
var blockExtractor = new BlockReader(_reader);
|
||||
if (blockExtractor.IsPacked && _dataPacked)
|
||||
{
|
||||
fileExtractor = new DeflateStream(blockExtractor, CompressionMode.Decompress);
|
||||
}
|
||||
else
|
||||
{
|
||||
fileExtractor = blockExtractor;
|
||||
}
|
||||
|
||||
if (blockExtractor.IsContainer && recursiveUnpack)
|
||||
{
|
||||
var outputDirectory = Path.Combine(destDir, element.Name);
|
||||
var tmpData = new MemoryStream(); // TODO: переделать MemoryStream --> FileStream
|
||||
fileExtractor.CopyTo(tmpData);
|
||||
tmpData.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
var internalContainer = new File8Reader(tmpData, dataPacked: false);
|
||||
internalContainer.ExtractAll(outputDirectory, recursiveUnpack);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Просто файл
|
||||
var outputFileName = Path.Combine(destDir, element.Name);
|
||||
using var outputFile = new FileStream(outputFileName, FileMode.Create);
|
||||
fileExtractor.CopyTo(outputFile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Извлекает все файлы из контейнера.
|
||||
/// </summary>
|
||||
/// <param name="destDir">Каталог назначения.</param>
|
||||
/// <param name="recursiveUnpack">Если установлен в Истина, то все найденные вложенные восьмофайлы
|
||||
/// будут распакованы в отдельные подкаталоги. Необязательный.</param>
|
||||
public void ExtractAll(string destDir, bool recursiveUnpack = false)
|
||||
{
|
||||
foreach (var element in Elements)
|
||||
{
|
||||
Extract(element, destDir, recursiveUnpack);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_reader.Close();
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
_reader.Close();
|
||||
}
|
||||
}
|
23
OneSTools.Common/Platform/Unpack/FileFormat.cs
Normal file
23
OneSTools.Common/Platform/Unpack/FileFormat.cs
Normal file
@ -0,0 +1,23 @@
|
||||
namespace OneSTools.Common.Platform.Unpack;
|
||||
|
||||
internal static partial class FileFormat
|
||||
{
|
||||
public const uint V8FfSignature = 0x7fffffff;
|
||||
public const uint V8DefaultPageSize = 512;
|
||||
|
||||
public static bool IsContainer(byte[] data)
|
||||
{
|
||||
var reader = new MemoryStream(data);
|
||||
try
|
||||
{
|
||||
ContainerHeader.Read(reader);
|
||||
BlockHeader.Read(reader);
|
||||
}
|
||||
catch (File8FormatException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
BIN
onecmonitor-agent/Asserts/ПодтверждениеЛегальности.epf
Normal file
BIN
onecmonitor-agent/Asserts/ПодтверждениеЛегальности.epf
Normal file
Binary file not shown.
@ -15,13 +15,20 @@ var host = Host.CreateDefaultBuilder(args)
|
||||
{
|
||||
options.ServiceName = "OnecMonitorAgent";
|
||||
});
|
||||
services.AddSystemd();
|
||||
services.AddSingleton<RasHolder>();
|
||||
services.AddSingleton<InfoBasesUpdater>();
|
||||
services.AddDbContext<AppDbContext>();
|
||||
|
||||
// Commands watcher connection
|
||||
services.AddSingleton<OnecMonitorConnection>();
|
||||
|
||||
services.AddSingleton<TechLogFolderWatcher>();
|
||||
services.AddSingleton<TechLogExporter>();
|
||||
services.AddHostedService<TechLogSeancesWatcher>();
|
||||
|
||||
services.AddSingleton<InfoBasesUpdateTasksQueue>();
|
||||
services.AddHostedService<InfoBasesUpdater>();
|
||||
|
||||
services.AddHostedService<CommandsWatcher>();
|
||||
})
|
||||
.Build();
|
||||
|
@ -4,6 +4,8 @@ using OnecMonitor.Agent.Services.InfoBases;
|
||||
using OnecMonitor.Agent.Services.TechLog;
|
||||
using OnecMonitor.Common.DTO;
|
||||
using OneSTools.Common.Platform;
|
||||
using OneSTools.Common.Platform.RemoteAdministration;
|
||||
using OneSTools.Common.Platform.Services;
|
||||
|
||||
namespace OnecMonitor.Agent.Services
|
||||
{
|
||||
@ -11,14 +13,14 @@ namespace OnecMonitor.Agent.Services
|
||||
{
|
||||
private readonly OnecMonitorConnection _server;
|
||||
private readonly AppDbContext _appDbContext;
|
||||
private readonly InfoBasesUpdater _infoBasesUpdater;
|
||||
private readonly InfoBasesUpdateTasksQueue _updateTasksQueue;
|
||||
private readonly RasHolder _rasHolder;
|
||||
private readonly TechLogExporter _techLogExporter;
|
||||
private readonly ILogger<CommandsWatcher> _logger;
|
||||
|
||||
public CommandsWatcher(
|
||||
IServiceProvider serviceProvider,
|
||||
InfoBasesUpdater infoBasesUpdater,
|
||||
InfoBasesUpdateTasksQueue updateTasksQueue,
|
||||
TechLogExporter techLogExporter,
|
||||
RasHolder rasHolder,
|
||||
ILogger<CommandsWatcher> logger)
|
||||
@ -28,7 +30,7 @@ namespace OnecMonitor.Agent.Services
|
||||
_server = scope.ServiceProvider.GetRequiredService<OnecMonitorConnection>();
|
||||
_appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
_techLogExporter = techLogExporter;
|
||||
_infoBasesUpdater = infoBasesUpdater;
|
||||
_updateTasksQueue = updateTasksQueue;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@ -92,22 +94,19 @@ namespace OnecMonitor.Agent.Services
|
||||
|
||||
private async Task SendInstalledPlatforms(Message message, CancellationToken cancellationToken)
|
||||
{
|
||||
_logger.LogTrace("Send installed platforms");
|
||||
|
||||
var platforms = V8Platforms.GetInstalledPlatforms();
|
||||
await _server.Send(MessageType.InstalledPlatforms, platforms, message, cancellationToken);
|
||||
}
|
||||
|
||||
private async Task HandleUpdateInfoBasesRequest(Message message, CancellationToken cancellationToken)
|
||||
{
|
||||
await _updateTasksQueue.QueueAsync(message, cancellationToken);
|
||||
await _server.SendOk(message, cancellationToken);
|
||||
_infoBasesUpdater.RequestInfoBasesUpdateTask();
|
||||
}
|
||||
|
||||
private async Task HandleUpdateSettingsRequest(Message message, CancellationToken cancellationToken)
|
||||
{
|
||||
await _server.SendOk(message, cancellationToken);
|
||||
|
||||
await UpdateSettings(cancellationToken);
|
||||
}
|
||||
|
||||
@ -127,8 +126,6 @@ namespace OnecMonitor.Agent.Services
|
||||
|
||||
private async Task SendV8Clusters(Message message, CancellationToken cancellationToken)
|
||||
{
|
||||
_logger.LogTrace("Send clusters");
|
||||
|
||||
var ragents = V8Services.GetActiveRagentServices();
|
||||
var clusters = new List<V8Cluster>();
|
||||
|
||||
@ -140,8 +137,6 @@ namespace OnecMonitor.Agent.Services
|
||||
|
||||
private async Task SendV8InfoBases(Message message, CancellationToken cancellationToken)
|
||||
{
|
||||
_logger.LogTrace("Send infobases");
|
||||
|
||||
var request = MessagePackSerializer.Deserialize<InfoBasesRequestDto>(message.Data, cancellationToken: cancellationToken);
|
||||
|
||||
var ragent = V8Services.GetActiveRagentForClusterPort(request.Cluster.Port);
|
||||
@ -156,23 +151,18 @@ namespace OnecMonitor.Agent.Services
|
||||
|
||||
private async Task SendRagentServices(Message message, CancellationToken cancellationToken)
|
||||
{
|
||||
_logger.LogTrace("Send ragent services");
|
||||
|
||||
var services = V8Services.GetRagentServices();
|
||||
await _server.Send(MessageType.RagentServices, services, message, cancellationToken);
|
||||
}
|
||||
|
||||
private async Task SendRasServices(Message message, CancellationToken cancellationToken)
|
||||
{
|
||||
_logger.LogTrace("Send ras services");
|
||||
|
||||
var services = _rasHolder.GetRasServices();
|
||||
await _server.Send(MessageType.RasServices, services, message, cancellationToken);
|
||||
}
|
||||
|
||||
private async Task UpdateTechLogSeancesByRequest(Message message, CancellationToken cancellationToken)
|
||||
{
|
||||
_logger.LogTrace("Updating tech log seances by server request");
|
||||
await _server.SendOk(message, cancellationToken);
|
||||
|
||||
if (_techLogExporter.Enabled)
|
||||
@ -181,14 +171,12 @@ namespace OnecMonitor.Agent.Services
|
||||
|
||||
private async Task UpdateTechLogSeances(CancellationToken cancellationToken)
|
||||
{
|
||||
_logger.LogTrace("Updating tech log seances");
|
||||
|
||||
try
|
||||
{
|
||||
var seances = await _server.Get<List<TechLogSeanceDto>>(
|
||||
MessageType.TechLogSeancesRequest,
|
||||
MessageType.TechLogSeances,
|
||||
cancellationToken);;
|
||||
cancellationToken);
|
||||
|
||||
await _appDbContext.Database.BeginTransactionAsync(cancellationToken);
|
||||
|
||||
@ -223,8 +211,6 @@ namespace OnecMonitor.Agent.Services
|
||||
await _appDbContext.Database.CommitTransactionAsync(cancellationToken);
|
||||
|
||||
await _appDbContext.SaveChangesAsync(cancellationToken);
|
||||
|
||||
_logger.LogTrace("Tech log seances updated");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -0,0 +1,15 @@
|
||||
using System.Threading.Channels;
|
||||
using OnecMonitor.Common.DTO;
|
||||
|
||||
namespace OnecMonitor.Agent.Services.InfoBases;
|
||||
|
||||
public class InfoBasesUpdateTasksQueue
|
||||
{
|
||||
private readonly Channel<Message> _updateRequestsChannel = Channel.CreateUnbounded<Message>();
|
||||
|
||||
public async Task QueueAsync(Message message, CancellationToken cancellationToken)
|
||||
=> await _updateRequestsChannel.Writer.WriteAsync(message, cancellationToken);
|
||||
|
||||
public async Task<Message> DequeueAsync(CancellationToken cancellationToken)
|
||||
=> await _updateRequestsChannel.Reader.ReadAsync(cancellationToken);
|
||||
}
|
@ -1,49 +1,73 @@
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using OnecMonitor.Agent.Extensions;
|
||||
using System.Threading.Channels;
|
||||
using OnecMonitor.Common.DTO;
|
||||
using OneSTools.Common.Designer.Batch;
|
||||
using OneSTools.Common.Extensions;
|
||||
using OneSTools.Common.Platform;
|
||||
using Org.BouncyCastle.Asn1.X509;
|
||||
using OneSTools.Common.Platform.RemoteAdministration;
|
||||
using OneSTools.Common.Platform.Services;
|
||||
|
||||
namespace OnecMonitor.Agent.Services.InfoBases;
|
||||
|
||||
public sealed class InfoBasesUpdater : IDisposable
|
||||
public sealed class InfoBasesUpdater : BackgroundService
|
||||
{
|
||||
private readonly InfoBasesUpdateTasksQueue _queue;
|
||||
|
||||
private readonly AsyncServiceScope _scope;
|
||||
private readonly OnecMonitorConnection _server;
|
||||
private readonly IHostApplicationLifetime _applicationLifetime;
|
||||
private readonly RasHolder _rasHolder;
|
||||
private readonly ILogger<InfoBasesUpdater> _logger;
|
||||
private bool _disposed;
|
||||
private readonly object _racLocker = new();
|
||||
|
||||
public InfoBasesUpdater(IServiceProvider serviceProvider, RasHolder rasHolder, IHostApplicationLifetime applicationLifetime, ILogger<InfoBasesUpdater> logger)
|
||||
public InfoBasesUpdater(
|
||||
IServiceProvider serviceProvider,
|
||||
InfoBasesUpdateTasksQueue tasksQueue,
|
||||
RasHolder rasHolder,
|
||||
IHostApplicationLifetime applicationLifetime,
|
||||
ILogger<InfoBasesUpdater> logger)
|
||||
{
|
||||
_scope = serviceProvider.CreateAsyncScope();
|
||||
_queue = tasksQueue;
|
||||
_server = _scope.ServiceProvider.GetRequiredService<OnecMonitorConnection>();
|
||||
_applicationLifetime = applicationLifetime;
|
||||
_rasHolder = rasHolder;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void RequestInfoBasesUpdateTask()
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
Task.Run(async () =>
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
var accessCode = "12345";
|
||||
var message = "Технические работы";
|
||||
|
||||
await _queue.DequeueAsync(stoppingToken);
|
||||
|
||||
try
|
||||
{
|
||||
await RequestInfoBasesUpdateTask(stoppingToken);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, $"Ошибка обработки задания обновления: {e.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RequestInfoBasesUpdateTask(CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
const string accessCode = "12345";
|
||||
const string message = "Технические работы";
|
||||
|
||||
var task = await _server.Get<UpdateInfoBaseTaskDto>(
|
||||
MessageType.UpdateInfoBasesTaskRequest,
|
||||
MessageType.UpdateInfoBasesTask,
|
||||
_applicationLifetime.ApplicationStopping);
|
||||
|
||||
var config = task.Configurations.FirstOrDefault(c => c.IsUpdate || c.IsConfiguration);
|
||||
var extensions = task.Configurations.Where(c => c.IsExtension).ToList();
|
||||
|
||||
var config = task.Files.FirstOrDefault(c => c.IsUpdate || c.IsConfiguration);
|
||||
var extensions = task.Files.Where(c => c.IsExtension).ToList();
|
||||
|
||||
var configurationsPaths = new Dictionary<string, string>();
|
||||
task.Configurations.ForEach(i =>
|
||||
task.Files.ForEach(i =>
|
||||
{
|
||||
var path = Path.Join(Path.GetTempPath(), $"{i.Id}.cfu") ;
|
||||
|
||||
@ -53,131 +77,159 @@ public sealed class InfoBasesUpdater : IDisposable
|
||||
file.Write(i.Data);
|
||||
file.Close();
|
||||
}
|
||||
|
||||
|
||||
configurationsPaths.Add(i.Id.ToString(), path);
|
||||
});
|
||||
|
||||
await Parallel.ForEachAsync(task.InfoBases, async (infoBase, cancellationToken) =>
|
||||
{
|
||||
var log = new List<UpdateInfoBaseTaskLogItemDto>();
|
||||
|
||||
try
|
||||
var tasks = new List<Task>();
|
||||
|
||||
foreach (var infoBase in task.InfoBases)
|
||||
{
|
||||
var ibTask = Task.Factory.StartNew(async () =>
|
||||
{
|
||||
var ragent = V8Services.GetActiveRagentForClusterPort(infoBase.Cluster.Port);
|
||||
var ras = _rasHolder.GetActiveRasForRagent(ragent);
|
||||
var rac = Rac.GetRacForRasService(ras);
|
||||
|
||||
var platform = ragent.Platform;
|
||||
|
||||
if (!platform.HasOnecV8)
|
||||
throw new Exception("Для платформы агента не установлен конфигуратор");
|
||||
|
||||
OnecV8BatchMode GetBatchDesigner()
|
||||
=> new(platform, $"{infoBase.Cluster.Host}:{infoBase.Cluster.Port}", infoBase.InfoBaseName);
|
||||
|
||||
OnecV8BatchMode GetBatchEnterprise()
|
||||
=> new(platform, $"{infoBase.Cluster.Host}:{infoBase.Cluster.Port}", infoBase.InfoBaseName, false);
|
||||
|
||||
await AddLogItemAndSend(task, infoBase, log, "Блокировка соединений и регламентных заданий", cancellationToken);
|
||||
|
||||
rac.BlockConnections(
|
||||
infoBase.Cluster.Id,
|
||||
infoBase.InfoBaseInternalId.ToString(),
|
||||
infoBase.Credentials.User,
|
||||
infoBase.Credentials.Password,
|
||||
accessCode,
|
||||
message);
|
||||
|
||||
await AddLogItemAndSend(task, infoBase, log, "Завершение сессий", cancellationToken);
|
||||
|
||||
var sessions = rac.GetInfoBaseSessions(infoBase.Cluster.Id, infoBase.InfoBaseInternalId.ToString());
|
||||
sessions
|
||||
.Where(c => c.AppId != "RAS")
|
||||
.ToList()
|
||||
.ForEach(s => rac.TerminateSession(infoBase.Cluster.Id, s.Id));
|
||||
|
||||
if (config != null)
|
||||
var log = new List<UpdateInfoBaseTaskLogItemDto>();
|
||||
|
||||
try
|
||||
{
|
||||
if (config.IsConfiguration)
|
||||
{
|
||||
await AddLogItemAndSend(task, infoBase, log, "Загрузка файла конфигурации", cancellationToken);
|
||||
var ragent = V8Services.GetActiveRagentForClusterPort(infoBase.Cluster.Port);
|
||||
var ras = _rasHolder.GetActiveRasForRagent(ragent);
|
||||
var rac = Rac.GetRacForRasService(ras);
|
||||
|
||||
var platform = ragent.Platform;
|
||||
|
||||
using var loadCfgBatch = GetBatchDesigner();
|
||||
loadCfgBatch.LoadConfiguration(
|
||||
configurationsPaths[config.Id.ToString()],
|
||||
if (!platform.HasOnecV8)
|
||||
throw new Exception("Для платформы агента не установлен конфигуратор");
|
||||
|
||||
OnecV8BatchMode GetBatchDesigner()
|
||||
=> new(platform, $"{infoBase.Cluster.Host}:{infoBase.Cluster.Port}", infoBase.InfoBaseName);
|
||||
|
||||
OnecV8BatchMode GetBatchEnterprise()
|
||||
=> new(platform, $"{infoBase.Cluster.Host}:{infoBase.Cluster.Port}", infoBase.InfoBaseName, false);
|
||||
|
||||
await AddLogItemAndSend(task, infoBase, log, "Блокировка соединений и регламентных заданий", cancellationToken);
|
||||
|
||||
lock (_racLocker)
|
||||
rac.BlockConnections(
|
||||
infoBase.Cluster.Id,
|
||||
infoBase.InfoBaseInternalId,
|
||||
infoBase.Credentials.User,
|
||||
infoBase.Credentials.Password,
|
||||
accessCode,
|
||||
message);
|
||||
|
||||
await AddLogItemAndSend(task, infoBase, log, "Завершение сессий", cancellationToken);
|
||||
|
||||
lock (_racLocker)
|
||||
{
|
||||
var sessions = rac.GetInfoBaseSessions(infoBase.Cluster.Id, infoBase.InfoBaseInternalId);
|
||||
sessions
|
||||
.Where(c => !c.AppId.Contains("RAS", StringComparison.CurrentCultureIgnoreCase))
|
||||
.ToList()
|
||||
.ForEach(s =>
|
||||
{
|
||||
try
|
||||
{
|
||||
rac.TerminateSession(infoBase.Cluster.Id, s.Id);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Игнорируем, т.к. сеанс уже мог быть закрыт, мог быть повисшим и т.п.
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (config != null)
|
||||
{
|
||||
if (config.IsConfiguration)
|
||||
{
|
||||
await AddLogItemAndSend(task, infoBase, log, "Загрузка файла конфигурации", cancellationToken);
|
||||
|
||||
using var loadCfgBatch = GetBatchDesigner();
|
||||
loadCfgBatch.LoadConfiguration(
|
||||
configurationsPaths[config.Id.ToString()],
|
||||
infoBase.Credentials.User,
|
||||
infoBase.Credentials.Password,
|
||||
accessCode,
|
||||
true);
|
||||
|
||||
await AddLogItemAndSend(task, infoBase, log, loadCfgBatch.OutFileContent, cancellationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
await AddLogItemAndSend(task, infoBase, log, "Обновление ИБ файлом обновления конфигурации", cancellationToken);
|
||||
|
||||
using var updateCfgBatch = GetBatchDesigner();
|
||||
updateCfgBatch.UpdateConfiguration(
|
||||
configurationsPaths[config.Id.ToString()],
|
||||
infoBase.Credentials.User,
|
||||
infoBase.Credentials.Password,
|
||||
accessCode,
|
||||
true);
|
||||
|
||||
await AddLogItemAndSend(task, infoBase, log, updateCfgBatch.OutFileContent, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
if (extensions.Count > 0)
|
||||
await AddLogItemAndSend(task, infoBase, log, "Загрузка расширений ИБ", cancellationToken);
|
||||
|
||||
foreach (var extension in extensions)
|
||||
{
|
||||
await AddLogItemAndSend(task, infoBase, log, $"Загрузка расширения {extension.Name}", cancellationToken);
|
||||
|
||||
using var loadExtBatch = GetBatchDesigner();
|
||||
loadExtBatch.LoadExtension(
|
||||
extension.Name,
|
||||
configurationsPaths[extension.Id.ToString()],
|
||||
infoBase.Credentials.User,
|
||||
infoBase.Credentials.Password,
|
||||
accessCode,
|
||||
true);
|
||||
|
||||
await AddLogItemAndSend(task, infoBase, log, loadCfgBatch.OutFileContent, cancellationToken);
|
||||
|
||||
await AddLogItemAndSend(task, infoBase, log, loadExtBatch.OutFileContent, cancellationToken);
|
||||
}
|
||||
else
|
||||
|
||||
var needAcceptLegalUsing = config != null;
|
||||
if (needAcceptLegalUsing)
|
||||
{
|
||||
await AddLogItemAndSend(task, infoBase, log, "Обновление ИБ файлом обновления конфигурации", cancellationToken);
|
||||
|
||||
using var updateCfgBatch = GetBatchDesigner();
|
||||
updateCfgBatch.UpdateConfiguration(
|
||||
configurationsPaths[config.Id.ToString()],
|
||||
await AddLogItemAndSend(task, infoBase, log, "Подтверждение легальности получения и запуск обработчиков обновления", cancellationToken);
|
||||
|
||||
var acceptLegalBatch = GetBatchEnterprise();
|
||||
var epfPath = GetExternalDataProcessorPath("ПодтверждениеЛегальности.epf");
|
||||
acceptLegalBatch.ExecuteExternalDataProcessor(
|
||||
epfPath,
|
||||
infoBase.Credentials.User,
|
||||
infoBase.Credentials.Password,
|
||||
accessCode,
|
||||
true);
|
||||
|
||||
await AddLogItemAndSend(task, infoBase, log, updateCfgBatch.OutFileContent, cancellationToken);
|
||||
}
|
||||
|
||||
await AddLogItemAndSend(task, infoBase, log, "Разблокировка соединений и регламентных заданий", cancellationToken);
|
||||
|
||||
lock (_racLocker)
|
||||
rac.UnblockConnections(
|
||||
infoBase.Cluster.Id,
|
||||
infoBase.InfoBaseInternalId,
|
||||
infoBase.Credentials.User,
|
||||
infoBase.Credentials.Password);
|
||||
|
||||
await AddLogItemAndSend(task, infoBase, log, "Обновление завершено", _applicationLifetime.ApplicationStopping, false, true);
|
||||
}
|
||||
|
||||
if (extensions.Count > 0)
|
||||
await AddLogItemAndSend(task, infoBase, log, "Загрузка расширений ИБ", cancellationToken);
|
||||
|
||||
foreach (var extension in extensions)
|
||||
catch (Exception e)
|
||||
{
|
||||
await AddLogItemAndSend(task, infoBase, log, $"Загрузка расширения {extension.Name}", cancellationToken);
|
||||
|
||||
using var loadExtBatch = GetBatchDesigner();
|
||||
loadExtBatch.LoadExtension(
|
||||
extension.Name,
|
||||
configurationsPaths[extension.Id.ToString()],
|
||||
infoBase.Credentials.User,
|
||||
infoBase.Credentials.Password,
|
||||
accessCode,
|
||||
true);
|
||||
|
||||
await AddLogItemAndSend(task, infoBase, log, loadExtBatch.OutFileContent, cancellationToken);
|
||||
await AddLogItemAndSend(task, infoBase, log, e.ToString(), _applicationLifetime.ApplicationStopping, true, true);
|
||||
}
|
||||
}, _applicationLifetime.ApplicationStopping);
|
||||
|
||||
tasks.Add(ibTask);
|
||||
}
|
||||
|
||||
var needAcceptLegalUsing = config != null;
|
||||
if (needAcceptLegalUsing)
|
||||
{
|
||||
await AddLogItemAndSend(task, infoBase, log, "Подтверждение легальности получения и запуск обработчиков обновления", cancellationToken);
|
||||
|
||||
var acceptLegalBatch = GetBatchEnterprise();
|
||||
var epfPath = GetExternalDataProcessorPath("ПодтверждениеЛегальности.epf");
|
||||
acceptLegalBatch.ExecuteExternalDataProcessor(
|
||||
epfPath,
|
||||
infoBase.Credentials.User,
|
||||
infoBase.Credentials.Password,
|
||||
accessCode,
|
||||
true);
|
||||
}
|
||||
|
||||
await AddLogItemAndSend(task, infoBase, log, "Разблокировка соединений и регламентных заданий", cancellationToken);
|
||||
|
||||
rac.UnblockConnections(
|
||||
infoBase.Cluster.Id,
|
||||
infoBase.InfoBaseInternalId.ToString(),
|
||||
infoBase.Credentials.User,
|
||||
infoBase.Credentials.Password);
|
||||
|
||||
await AddLogItemAndSend(task, infoBase, log, "Обновление завершено", cancellationToken, false, true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await AddLogItemAndSend(task, infoBase, log, e.ToString(), cancellationToken, true, true);
|
||||
}
|
||||
});
|
||||
});
|
||||
Task.WaitAll(tasks.ToArray(), cancellationToken);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task AddLogItemAndSend(
|
||||
@ -199,13 +251,14 @@ public sealed class InfoBasesUpdater : IDisposable
|
||||
InfoBaseId = infoBase.Id,
|
||||
TaskId = task.Id,
|
||||
});
|
||||
|
||||
await SendLog(log, cancellationToken);
|
||||
}
|
||||
|
||||
private async Task SendLog(List<UpdateInfoBaseTaskLogItemDto> log, CancellationToken cancellationToken)
|
||||
=> await _server.Send(MessageType.UpdateInfoBaseTaskLog, log, cancellationToken);
|
||||
|
||||
private string GetExternalDataProcessorPath(string fileName)
|
||||
private static string GetExternalDataProcessorPath(string fileName)
|
||||
=> Path.Join(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Asserts", fileName);
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
@ -222,14 +275,11 @@ public sealed class InfoBasesUpdater : IDisposable
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
public override Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
~InfoBasesUpdater()
|
||||
{
|
||||
Dispose(false);
|
||||
_scope.Dispose();
|
||||
_server.Dispose();
|
||||
|
||||
return base.StopAsync(cancellationToken);
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using OneSTools.Common.Extensions;
|
||||
using OneSTools.Common.Platform;
|
||||
using OneSTools.Common.Platform.Services;
|
||||
|
||||
namespace OnecMonitor.Agent.Services.InfoBases;
|
||||
|
||||
@ -63,7 +64,7 @@ public class RasHolder : IDisposable
|
||||
_processes.Remove(process);
|
||||
};
|
||||
|
||||
_processes.Add(process!);
|
||||
_processes.Add(process);
|
||||
|
||||
var serviceModel = new RasService
|
||||
{
|
||||
|
@ -9,7 +9,8 @@ namespace OnecMonitor.Agent.Services
|
||||
{
|
||||
public class OnecMonitorConnection : ServerConnection
|
||||
{
|
||||
public OnecMonitorConnection(IServiceProvider serviceProvider, IHostApplicationLifetime hostApplicationLifetime)
|
||||
public OnecMonitorConnection(IServiceProvider serviceProvider, IHostApplicationLifetime hostApplicationLifetime)
|
||||
: base(serviceProvider.GetRequiredService<ILogger<OnecMonitorConnection>>())
|
||||
{
|
||||
var configuration = serviceProvider.GetRequiredService<IConfiguration>();
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Default": "Trace",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
|
@ -33,6 +33,7 @@
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.1" />
|
||||
<PackageReference Include="System.Diagnostics.PerformanceCounter" Version="9.0.1" />
|
||||
</ItemGroup>
|
||||
|
@ -9,6 +9,6 @@ public class UpdateInfoBaseTaskDto
|
||||
public Guid Id { get; set; }
|
||||
[Key(1)]
|
||||
public List<InfoBaseDto> InfoBases { get; set; } = [];
|
||||
[Key(4)]
|
||||
public List<ConfigurationDto> Configurations { get; set; } = [];
|
||||
[Key(2)]
|
||||
public List<V8FileDto> Files { get; set; } = [];
|
||||
}
|
@ -3,7 +3,7 @@ using MessagePack;
|
||||
namespace OnecMonitor.Common.DTO;
|
||||
|
||||
[MessagePackObject]
|
||||
public class ConfigurationDto
|
||||
public class V8FileDto
|
||||
{
|
||||
[Key(0)]
|
||||
public Guid Id { get; set; }
|
@ -6,13 +6,15 @@ using System.Diagnostics.SymbolStore;
|
||||
using System.Net.Sockets;
|
||||
using System.Reflection.PortableExecutable;
|
||||
using System.Threading.Channels;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace OnecMonitor.Common;
|
||||
|
||||
public abstract class FastConnection : IDisposable
|
||||
public abstract class FastConnection(ILogger<FastConnection> logger) : IDisposable
|
||||
{
|
||||
protected Socket? Socket;
|
||||
|
||||
private CancellationToken _cancellationToken;
|
||||
|
||||
private readonly ConcurrentDictionary<Guid, TaskCompletionSource<Message>> _calls = new();
|
||||
private readonly Channel<Message> _inputChannel = Channel.CreateBounded<Message>(1000);
|
||||
private readonly Channel<Message> _outputChannel = Channel.CreateBounded<Message>(1000);
|
||||
@ -33,8 +35,10 @@ public abstract class FastConnection : IDisposable
|
||||
|
||||
protected void RunStreamLoops(CancellationToken cancellationToken)
|
||||
{
|
||||
_ = StartWritingToStream(cancellationToken);
|
||||
_ = StartReadingFromStream(cancellationToken);
|
||||
_cancellationToken = cancellationToken;
|
||||
|
||||
Task.Factory.StartNew(StartWritingToStream, TaskCreationOptions.LongRunning);
|
||||
Task.Factory.StartNew(StartReadingFromStream, TaskCreationOptions.LongRunning);
|
||||
|
||||
_disconnectingEventSemaphore.Release();
|
||||
}
|
||||
@ -113,11 +117,14 @@ public abstract class FastConnection : IDisposable
|
||||
{
|
||||
var cts = new TaskCompletionSource<Message>();
|
||||
_calls.TryAdd(message.Header.CallId, cts);
|
||||
|
||||
|
||||
logger.LogTrace($"Постановка сообщения в очередь отправки. Тип: {message.Header.Type}. Идентификатор: {message.Header.CallId}");
|
||||
await _outputChannel.Writer.WriteAsync(message, cancellationToken);
|
||||
|
||||
|
||||
logger.LogTrace($"Ожидание подтверждения на сообщение. Тип: {message.Header.Type}. Идентификатор: {message.Header.CallId}");
|
||||
var result = await cts.Task.WaitAsync(cancellationToken);
|
||||
|
||||
|
||||
logger.LogTrace($"Подтверждение сообщения получено. Тип: {message.Header.Type}. Идентификатор: {message.Header.CallId}");
|
||||
if (result == null)
|
||||
throw new TimeoutException("Ошибка получения ответа на вызов");
|
||||
|
||||
@ -139,11 +146,14 @@ public abstract class FastConnection : IDisposable
|
||||
{
|
||||
var cts = new TaskCompletionSource<Message>();
|
||||
_calls.TryAdd(message.Header.CallId, cts);
|
||||
|
||||
|
||||
logger.LogTrace($"Постановка сообщения в очередь отправки. Тип: {message.Header.Type}. Идентификатор: {message.Header.CallId}");
|
||||
await _outputChannel.Writer.WriteAsync(message, cancellationToken);
|
||||
|
||||
|
||||
logger.LogTrace($"Ожидание подтверждения на сообщение. Тип: {message.Header.Type}. Идентификатор: {message.Header.CallId}");
|
||||
var result = await cts.Task.WaitAsync(cancellationToken);
|
||||
|
||||
|
||||
logger.LogTrace($"Подтверждение сообщения получено. Тип: {message.Header.Type}. Идентификатор: {message.Header.CallId}");
|
||||
if (result == null)
|
||||
throw new TimeoutException("Ошибка получения ответа на вызов");
|
||||
|
||||
@ -167,6 +177,8 @@ public abstract class FastConnection : IDisposable
|
||||
|
||||
if (header.Length > 0)
|
||||
await Socket!.SendAsync(data, cancellationToken);
|
||||
|
||||
logger.LogTrace($"Отправлено сообщение в поток. Тип: {header.Type}. Идентификатор: {header.CallId}");
|
||||
}
|
||||
catch
|
||||
{
|
||||
@ -174,18 +186,20 @@ public abstract class FastConnection : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
private async Task StartWritingToStream(CancellationToken cancellationToken)
|
||||
private async Task StartWritingToStream()
|
||||
{
|
||||
try
|
||||
{
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
while (!_cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
var item = await _outputChannel.Reader.ReadAsync(cancellationToken);
|
||||
var message = await _outputChannel.Reader.ReadAsync(_cancellationToken);
|
||||
|
||||
await Socket!.SendAsync(item.Header.AsMemory(), cancellationToken);
|
||||
await Socket!.SendAsync(message.Header.AsMemory(), _cancellationToken);
|
||||
|
||||
if (item.Data.Length > 0)
|
||||
await Socket!.SendAsync(item.Data, cancellationToken);
|
||||
if (message.Data.Length > 0)
|
||||
await Socket!.SendAsync(message.Data, _cancellationToken);
|
||||
|
||||
logger.LogTrace($"Отправлено сообщение в поток. Тип: {message.Header.Type}. Идентификатор: {message.Header.CallId}");
|
||||
}
|
||||
}
|
||||
catch
|
||||
@ -194,29 +208,31 @@ public abstract class FastConnection : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
private async Task StartReadingFromStream(CancellationToken cancellationToken)
|
||||
private async Task StartReadingFromStream()
|
||||
{
|
||||
try
|
||||
{
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
while (!_cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
var headerBuffer = await ReadBytesFromStream(MessageHeader.HeaderLength, cancellationToken);
|
||||
var headerBuffer = await ReadBytesFromStream(MessageHeader.HeaderLength, _cancellationToken);
|
||||
var header = MessageHeader.FromSpan(headerBuffer.Span);
|
||||
|
||||
Message message;
|
||||
|
||||
if (header.Length > 0)
|
||||
{
|
||||
var dataBuffer = await ReadBytesFromStream(header.Length, cancellationToken);
|
||||
var dataBuffer = await ReadBytesFromStream(header.Length, _cancellationToken);
|
||||
message = new Message(header, dataBuffer);
|
||||
}
|
||||
else
|
||||
message = new Message(header);
|
||||
|
||||
logger.LogTrace($"Получено сообщение из потока. Тип: {header.Type}. Идентификатор: {header.CallId}");
|
||||
|
||||
if (_calls.TryGetValue(message.Header.CallId, out var cts))
|
||||
cts.TrySetResult(message);
|
||||
else
|
||||
await _inputChannel.Writer.WriteAsync(message, cancellationToken);
|
||||
await _inputChannel.Writer.WriteAsync(message, _cancellationToken);
|
||||
}
|
||||
}
|
||||
catch
|
||||
|
34
onecmonitor-common/Protos/onecmonitor.proto
Normal file
34
onecmonitor-common/Protos/onecmonitor.proto
Normal file
@ -0,0 +1,34 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option csharp_namespace = "OnecMonitor.Common";
|
||||
|
||||
service OnecMonitorService {
|
||||
|
||||
}
|
||||
|
||||
message AgentInstanceDto {
|
||||
string id = 1;
|
||||
string instanceName = 2;
|
||||
double UtcOffset = 3;
|
||||
}
|
||||
|
||||
message ClusterDto {
|
||||
string id = 1;
|
||||
string host = 2;
|
||||
int32 port = 3;
|
||||
}
|
||||
|
||||
message CredentialsDto {
|
||||
string user = 1;
|
||||
string password = 2;
|
||||
}
|
||||
|
||||
message InfoBaseDto {
|
||||
string id = 1;
|
||||
string internalId = 2;
|
||||
string infoBaseName = 3;
|
||||
CredentialsDto credentials = 4;
|
||||
string publishAddress = 5;
|
||||
ClusterDto cluster = 6;
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace OnecMonitor.Common;
|
||||
|
||||
public class ServerConnection : FastConnection
|
||||
public class ServerConnection(ILogger<ServerConnection> logger) : FastConnection(logger)
|
||||
{
|
||||
private ILogger<ServerConnection> _logger = null!;
|
||||
private string _host = null!;
|
||||
|
@ -4,6 +4,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using OnecMonitor.Server.Converters.Sqlite;
|
||||
using OnecMonitor.Server.Models;
|
||||
using OnecMonitor.Common.DTO;
|
||||
using OnecMonitor.Server.Models.MaintenanceTasks;
|
||||
|
||||
namespace OnecMonitor.Server
|
||||
{
|
||||
@ -15,13 +16,15 @@ namespace OnecMonitor.Server
|
||||
public DbSet<LogTemplate> LogTemplates { get; set; }
|
||||
public DbSet<TechLogSeance> TechLogSeances { get; set; }
|
||||
public DbSet<TechLogFilter> TechLogFilters { get; set; }
|
||||
public DbSet<V8Configuration> Configurations { get; set; }
|
||||
public DbSet<V8File> V8Files { get; set; }
|
||||
public DbSet<Credentials> Credentials { get; set; }
|
||||
public DbSet<InfoBase> InfoBases { get; set; }
|
||||
public DbSet<Cluster> Clusters { get; set; }
|
||||
public DbSet<UpdateInfoBaseTask> UpdateInfoBaseTasks { get; set; }
|
||||
public DbSet<UpdateInfoBaseTaskLogItem> UpdateInfoBaseTaskLogItems { get; set; }
|
||||
public DbSet<TechLogSettings> TechLogSettings { get; set; }
|
||||
public DbSet<MaintenanceTask> MaintenanceTasks { get; set; }
|
||||
public DbSet<MaintenanceStepNode> MaintenanceStepNodes { get; set; }
|
||||
|
||||
public AppDbContext(IHostEnvironment hostEnvironment)
|
||||
=> DbPath = Path.Join(hostEnvironment.ContentRootPath, "om-server.db");
|
||||
|
@ -1,10 +1,14 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using AutoMapper;
|
||||
using OnecMonitor.Server.Models;
|
||||
using OnecMonitor.Server.Models.MaintenanceTasks;
|
||||
using OnecMonitor.Server.ViewModels.Agents;
|
||||
using OnecMonitor.Server.ViewModels.Clusters;
|
||||
using OnecMonitor.Server.ViewModels.Configurations;
|
||||
using OnecMonitor.Server.ViewModels.V8Files;
|
||||
using OnecMonitor.Server.ViewModels.Credentials;
|
||||
using OnecMonitor.Server.ViewModels.InfoBases;
|
||||
using OnecMonitor.Server.ViewModels.MaintenanceTasks;
|
||||
using OnecMonitor.Server.ViewModels.TechLogSeances;
|
||||
using OnecMonitor.Server.ViewModels.TechLogSettings;
|
||||
using OnecMonitor.Server.ViewModels.UpdateInfoBaseTasks;
|
||||
@ -22,7 +26,7 @@ public class CommonProfile : Profile
|
||||
CreateMap<Cluster, SelectableItemViewModel>().ReverseMap();
|
||||
CreateMap<LogTemplate, SelectableItemViewModel>().ReverseMap();
|
||||
CreateMap<Credentials, SelectableItemViewModel>().ReverseMap();
|
||||
CreateMap<V8Configuration, SelectableItemViewModel>()
|
||||
CreateMap<V8File, SelectableItemViewModel>()
|
||||
.ForMember(c => c.Name, opt => opt.MapFrom(src => src.ToString()))
|
||||
.ReverseMap();
|
||||
|
||||
@ -36,11 +40,11 @@ public class CommonProfile : Profile
|
||||
.ForMember(c => c.InfoBases, i => i.Ignore())
|
||||
.ForMember(c => c.Clusters, i => i.Ignore());
|
||||
|
||||
CreateMap<V8Configuration, ConfigurationListItemViewModel>()
|
||||
CreateMap<V8File, V8FileListItemViewModel>()
|
||||
.ReverseMap()
|
||||
.ForMember(c => c.Id, i => i.Ignore());
|
||||
|
||||
CreateMap<V8Configuration, ConfigurationEditViewModel>()
|
||||
CreateMap<V8File, V8FileEditViewModel>()
|
||||
.ReverseMap()
|
||||
.ForMember(c => c.Id, i => i.Ignore());
|
||||
|
||||
@ -67,7 +71,7 @@ public class CommonProfile : Profile
|
||||
.ForMember(c => c.Id, i => i.Ignore())
|
||||
.ForMember(c => c.Cluster, i => i.Ignore());
|
||||
|
||||
CreateMap<V8Configuration, ConfigurationEditViewModel>()
|
||||
CreateMap<V8File, V8FileEditViewModel>()
|
||||
.ReverseMap()
|
||||
.ForMember(c => c.Id, i => i.Ignore());
|
||||
|
||||
@ -79,12 +83,32 @@ public class CommonProfile : Profile
|
||||
CreateMap<UpdateInfoBaseTask, UpdateInfoBaseTaskEditViewModel>()
|
||||
.ReverseMap()
|
||||
.ForMember(c => c.Id, i => i.Ignore())
|
||||
.ForMember(c => c.Configurations, i => i.Ignore())
|
||||
.ForMember(c => c.Files, i => i.Ignore())
|
||||
.ForMember(c => c.InfoBases, i => i.Ignore())
|
||||
.ForMember(c => c.Log, i => i.Ignore());
|
||||
|
||||
CreateMap<TechLogSettings, TechLogSettingsEditViewModel>()
|
||||
.ReverseMap()
|
||||
.ForMember(c => c.Id, i => i.Ignore());
|
||||
|
||||
CreateMap<MaintenanceTask, MaintenanceTaskEditViewModel>()
|
||||
.ReverseMap()
|
||||
.ForMember(c => c.Id, i => i.Ignore())
|
||||
.ForMember(c => c.InfoBases, i => i.Ignore());
|
||||
|
||||
CreateMap<MaintenanceTask, MaintenanceTaskListItemViewModel>().ReverseMap();
|
||||
|
||||
CreateMap<MaintenanceStep, MaintenanceStepViewModel>()
|
||||
.ReverseMap()
|
||||
.ForMember(c => c.Id, i => i.Ignore())
|
||||
.ForMember(c => c.File, i => i.Ignore());
|
||||
|
||||
CreateMap<MaintenanceStepNode, MaintenanceStepNodeViewModel>()
|
||||
.ForMember(c => c.StepId, i => i.MapFrom(src => src.Step.Id))
|
||||
.ReverseMap()
|
||||
.ForMember(c => c.Id, i => i.Ignore())
|
||||
.ForMember(c => c.LeftNode, i => i.Ignore())
|
||||
.ForMember(c => c.RightNode, i => i.Ignore())
|
||||
.ForMember(c => c.Step, i => i.Ignore());
|
||||
}
|
||||
}
|
@ -14,7 +14,7 @@ public class DtoProfile : Profile
|
||||
.ForMember(c => c.Id, opt => opt.MapFrom(src => src.ClusterInternalId))
|
||||
.ReverseMap();
|
||||
|
||||
CreateMap<V8Configuration, ConfigurationDto>()
|
||||
CreateMap<V8File, V8FileDto>()
|
||||
.ForMember(c => c.Data, opt => opt.MapFrom(src => File.ReadAllBytes(src.DataPath)))
|
||||
.ReverseMap();
|
||||
|
||||
|
@ -38,7 +38,7 @@ public class CredentialsController(AppDbContext appDbContext, IMapper mapper) :
|
||||
if (!ModelState.IsValid)
|
||||
return View("Edit", await PrepareViewModel(vm, cancellationToken));
|
||||
|
||||
var model = isNew ? new Credentials()
|
||||
var model = isNew ? new Credentials
|
||||
{
|
||||
Id = Guid.NewGuid()
|
||||
} : await appDbContext.Credentials
|
||||
|
@ -1,73 +0,0 @@
|
||||
using AutoMapper;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using OnecMonitor.Server.Helpers;
|
||||
using OnecMonitor.Server.Models;
|
||||
using OnecMonitor.Server.ViewModels.Maintenance;
|
||||
|
||||
namespace OnecMonitor.Server.Controllers;
|
||||
|
||||
public class MaintenanceController(AppDbContext appDbContext, IMapper mapper) : Controller
|
||||
{
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
return View(new MaintenanceIndexViewModel());
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Edit(Guid id, CancellationToken cancellationToken)
|
||||
{
|
||||
return View(await PrepareViewModel(new MaintenanceEditViewModel(), cancellationToken));
|
||||
}
|
||||
|
||||
public IActionResult Step(MaintenanceStepViewModel vm)
|
||||
=> vm.Kind switch
|
||||
{
|
||||
MaintenanceStepKind.LockConnections
|
||||
or MaintenanceStepKind.LoadExtension
|
||||
or MaintenanceStepKind.UpdateConfiguration
|
||||
or MaintenanceStepKind.LoadConfiguration
|
||||
or MaintenanceStepKind.StartExternalDataProcessor => PartialView($"Steps/{vm.Kind}", vm),
|
||||
MaintenanceStepKind.UnlockConnections or MaintenanceStepKind.CloseConnections
|
||||
or MaintenanceStepKind.UpdateDatabase => Ok(),
|
||||
_ => NotFound()
|
||||
};
|
||||
|
||||
public IActionResult ValidateStep(MaintenanceStepViewModel vm)
|
||||
{
|
||||
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
|
||||
switch (vm.Kind)
|
||||
{
|
||||
case MaintenanceStepKind.LoadExtension:
|
||||
case MaintenanceStepKind.UpdateConfiguration:
|
||||
case MaintenanceStepKind.LoadConfiguration:
|
||||
case MaintenanceStepKind.StartExternalDataProcessor:
|
||||
if (vm.FileId == null || vm.FileId == Guid.Empty)
|
||||
ModelState.AddModelError(nameof(vm.FileId), "Не указан файл");
|
||||
break;
|
||||
case MaintenanceStepKind.LockConnections:
|
||||
if (string.IsNullOrEmpty(vm.AccessCode))
|
||||
ModelState.AddModelError(nameof(vm.AccessCode), "Не указан код доступа");
|
||||
if (string.IsNullOrEmpty(vm.Message))
|
||||
ModelState.AddModelError(nameof(vm.Message), "Не указан тест сообщения");
|
||||
break;
|
||||
}
|
||||
|
||||
var view = PartialView($"Steps/{vm.Kind}", vm);
|
||||
view.StatusCode = ModelState.IsValid ? 200 : 400;
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
private async Task<MaintenanceEditViewModel> PrepareViewModel(MaintenanceEditViewModel vm, CancellationToken cancellationToken)
|
||||
{
|
||||
vm.AvailableInfoBases = await UiHelper.SelectableItemsFrom(
|
||||
appDbContext.InfoBases,
|
||||
vm.InfoBases,
|
||||
mapper,
|
||||
cancellationToken);
|
||||
|
||||
vm.MaintenanceStepKinds = UiHelper.SelectListFromEnum<MaintenanceStepKind>();
|
||||
|
||||
return vm;
|
||||
}
|
||||
}
|
182
onecmonitor-server/Controllers/MaintenanceTasksController.cs
Normal file
182
onecmonitor-server/Controllers/MaintenanceTasksController.cs
Normal file
@ -0,0 +1,182 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using AutoMapper;
|
||||
using AutoMapper.QueryableExtensions;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using OnecMonitor.Server.Helpers;
|
||||
using OnecMonitor.Server.Models;
|
||||
using OnecMonitor.Server.Models.MaintenanceTasks;
|
||||
using OnecMonitor.Server.ViewModels;
|
||||
using OnecMonitor.Server.ViewModels.MaintenanceTasks;
|
||||
|
||||
namespace OnecMonitor.Server.Controllers;
|
||||
|
||||
public class MaintenanceTasksController(AppDbContext appDbContext, IMapper mapper) : Controller
|
||||
{
|
||||
private readonly JsonSerializerOptions _jsonOptions = new()
|
||||
{
|
||||
PropertyNameCaseInsensitive = true,
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
|
||||
};
|
||||
|
||||
public async Task<IActionResult> Index(CancellationToken cancellationToken)
|
||||
=> View(new MaintenanceTasksIndexViewModel
|
||||
{
|
||||
Items = await appDbContext.MaintenanceTasks
|
||||
.AsNoTracking()
|
||||
.ProjectTo<MaintenanceTaskListItemViewModel>(mapper.ConfigurationProvider)
|
||||
.ToListAsync(cancellationToken)
|
||||
});
|
||||
|
||||
public async Task<IActionResult> Edit(Guid id, CancellationToken cancellationToken)
|
||||
{
|
||||
if (id == Guid.Empty)
|
||||
return View(await PrepareViewModel(new MaintenanceTaskEditViewModel(), cancellationToken));
|
||||
|
||||
var model = await appDbContext.MaintenanceTasks
|
||||
.AsNoTracking()
|
||||
.Include(c => c.RootNode)
|
||||
.ThenInclude(c => c.Step)
|
||||
.AsNoTracking()
|
||||
.Include(c => c.InfoBases)
|
||||
.FirstOrDefaultAsync(c => c.Id == id, cancellationToken);
|
||||
|
||||
if (model == null)
|
||||
return NotFound();
|
||||
|
||||
await LoadNodesRecursively(model.RootNode, cancellationToken);
|
||||
|
||||
var vm = mapper.Map<MaintenanceTaskEditViewModel>(model);
|
||||
var rootNodeVm = mapper.Map<MaintenanceStepNodeViewModel>(model.RootNode);
|
||||
vm.SerializedStepNode = JsonSerializer.Serialize(rootNodeVm, _jsonOptions);
|
||||
|
||||
return View(await PrepareViewModel(vm, cancellationToken));
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> Save(MaintenanceTaskEditViewModel vm, CancellationToken cancellationToken)
|
||||
{
|
||||
var isNew = vm.Id == Guid.Empty;
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
return View("Edit", await PrepareViewModel(vm, cancellationToken));
|
||||
|
||||
var model = isNew ? new MaintenanceTask
|
||||
{
|
||||
Id = Guid.NewGuid()
|
||||
} : await appDbContext.MaintenanceTasks
|
||||
.Include(c => c.RootNode)
|
||||
.FirstOrDefaultAsync(i => i.Id == vm.Id, cancellationToken);
|
||||
|
||||
if (model == null)
|
||||
return NotFound();
|
||||
|
||||
mapper.Map(vm, model);
|
||||
model.RootNode = JsonSerializer.Deserialize<MaintenanceStepNode>(vm.SerializedStepNode, _jsonOptions)!;
|
||||
model.RootNodeId = model.RootNode.Id;
|
||||
|
||||
await UiHelper.UpdateModelItems(appDbContext.InfoBases, vm.InfoBases, model.InfoBases, cancellationToken);
|
||||
|
||||
if (isNew)
|
||||
appDbContext.MaintenanceTasks.Add(model);
|
||||
else
|
||||
appDbContext.MaintenanceTasks.Update(model);
|
||||
|
||||
await appDbContext.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> EditStep(CancellationToken cancellationToken)
|
||||
{
|
||||
var vm = await HttpContext.Request.ReadFromJsonAsync<MaintenanceStepViewModel>(_jsonOptions, cancellationToken);
|
||||
return await UpdateStep(vm!, cancellationToken);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> UpdateStep(MaintenanceStepViewModel vm, CancellationToken cancellationToken)
|
||||
=> PartialView("EditStep", await PrepareViewModel(vm, cancellationToken));
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> ValidateStep(MaintenanceStepViewModel vm, CancellationToken cancellationToken)
|
||||
{
|
||||
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
|
||||
switch (vm.Kind)
|
||||
{
|
||||
case MaintenanceStepKind.LoadExtension:
|
||||
case MaintenanceStepKind.UpdateConfiguration:
|
||||
case MaintenanceStepKind.LoadConfiguration:
|
||||
case MaintenanceStepKind.StartExternalDataProcessor:
|
||||
if (vm.FileId == null || vm.FileId == Guid.Empty)
|
||||
ModelState.AddModelError(nameof(vm.FileId), "Не указан файл");
|
||||
break;
|
||||
case MaintenanceStepKind.LockConnections:
|
||||
if (string.IsNullOrEmpty(vm.AccessCode))
|
||||
ModelState.AddModelError(nameof(vm.AccessCode), "Не указан код доступа");
|
||||
if (string.IsNullOrEmpty(vm.Message))
|
||||
ModelState.AddModelError(nameof(vm.Message), "Не указан тест сообщения");
|
||||
break;
|
||||
}
|
||||
|
||||
if (ModelState.IsValid)
|
||||
return Json(vm);
|
||||
|
||||
var view = PartialView("EditStep", await PrepareViewModel(vm, cancellationToken));
|
||||
view.StatusCode = 400;
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
private async Task<MaintenanceTaskEditViewModel> PrepareViewModel(MaintenanceTaskEditViewModel vm, CancellationToken cancellationToken)
|
||||
{
|
||||
vm.AvailableInfoBases = await UiHelper.SelectableItemsFrom(
|
||||
appDbContext.InfoBases,
|
||||
vm.InfoBases,
|
||||
mapper,
|
||||
cancellationToken);
|
||||
|
||||
return vm;
|
||||
}
|
||||
|
||||
private async Task<MaintenanceStepViewModel> PrepareViewModel(MaintenanceStepViewModel vm, CancellationToken cancellationToken)
|
||||
{
|
||||
vm.Kinds = UiHelper.SelectListFromEnum<MaintenanceStepKind>(vm.Kind);
|
||||
|
||||
var files = vm.Kind switch
|
||||
{
|
||||
MaintenanceStepKind.LoadConfiguration => appDbContext.V8Files.Where(c => c.IsConfiguration),
|
||||
MaintenanceStepKind.LoadExtension => appDbContext.V8Files.Where(c => c.IsExtension),
|
||||
MaintenanceStepKind.UpdateConfiguration => appDbContext.V8Files.Where(c => c.IsUpdate),
|
||||
MaintenanceStepKind.StartExternalDataProcessor => appDbContext.V8Files.Where(c => c.IsExternalDataProcessor),
|
||||
_ => null
|
||||
};
|
||||
|
||||
if (files is not null)
|
||||
vm.Files = await UiHelper.SelectListFrom(files, c => c.ToString(), vm.FileId, cancellationToken);
|
||||
|
||||
return vm;
|
||||
}
|
||||
|
||||
private async Task LoadNodesRecursively(MaintenanceStepNode node, CancellationToken cancellationToken)
|
||||
{
|
||||
if (node.LeftNodeId != null && node.LeftNodeId != Guid.Empty)
|
||||
node.LeftNode = await LoadNodeRecursively(node.LeftNodeId, cancellationToken);
|
||||
|
||||
if (node.RightNodeId != null && node.RightNodeId != Guid.Empty)
|
||||
node.RightNode = await LoadNodeRecursively(node.RightNodeId, cancellationToken);
|
||||
}
|
||||
|
||||
private async Task<MaintenanceStepNode> LoadNodeRecursively(Guid? id, CancellationToken cancellationToken)
|
||||
{
|
||||
var node = (await appDbContext.MaintenanceStepNodes
|
||||
.AsNoTracking()
|
||||
.Include(c => c.Step)
|
||||
.FirstOrDefaultAsync(c => c.Id == id, cancellationToken))!;
|
||||
|
||||
await LoadNodesRecursively(node, cancellationToken);
|
||||
|
||||
return node;
|
||||
}
|
||||
}
|
@ -27,7 +27,7 @@ public class UpdateInfoBaseTasksController(AppDbContext appDbContext, AgentsConn
|
||||
var vm = id != Guid.Empty
|
||||
? await appDbContext.UpdateInfoBaseTasks
|
||||
.AsNoTracking()
|
||||
.Include(c => c.Configurations)
|
||||
.Include(c => c.Files)
|
||||
.Include(c => c.InfoBases)
|
||||
.ProjectTo<UpdateInfoBaseTaskEditViewModel>(mapper.ConfigurationProvider)
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
@ -43,8 +43,8 @@ public class UpdateInfoBaseTasksController(AppDbContext appDbContext, AgentsConn
|
||||
{
|
||||
var isNew = vm.Id == Guid.Empty;
|
||||
|
||||
var configIds = vm.Configurations.Select(c => c.Id).ToList();
|
||||
var configs = appDbContext.Configurations
|
||||
var configIds = vm.Files.Select(c => c.Id).ToList();
|
||||
var configs = appDbContext.V8Files
|
||||
.AsNoTracking()
|
||||
.Where(c => configIds.Contains(c.Id.ToString()))
|
||||
.ToList();
|
||||
@ -52,7 +52,7 @@ public class UpdateInfoBaseTasksController(AppDbContext appDbContext, AgentsConn
|
||||
var countOfUpdatesAndConfigs = configs.Count(c => c.IsUpdate || c.IsConfiguration);
|
||||
if (countOfUpdatesAndConfigs > 1)
|
||||
ModelState.AddModelError(
|
||||
nameof(UpdateInfoBaseTaskEditViewModel.Configurations),
|
||||
nameof(UpdateInfoBaseTaskEditViewModel.Files),
|
||||
"Список конфигураций может содержать только одну конфигурацию или обновление конфигурации");
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
@ -63,7 +63,7 @@ public class UpdateInfoBaseTasksController(AppDbContext appDbContext, AgentsConn
|
||||
Id = Guid.NewGuid()
|
||||
} : await appDbContext.UpdateInfoBaseTasks
|
||||
.Include(c => c.InfoBases)
|
||||
.Include(c => c.Configurations)
|
||||
.Include(c => c.Files)
|
||||
.Include(c => c.Log)
|
||||
.FirstOrDefaultAsync(i => i.Id == vm.Id, cancellationToken);
|
||||
|
||||
@ -76,7 +76,7 @@ public class UpdateInfoBaseTasksController(AppDbContext appDbContext, AgentsConn
|
||||
mapper.Map(vm, model);
|
||||
|
||||
await UiHelper.UpdateModelItems(appDbContext.InfoBases, vm.InfoBases, model.InfoBases, cancellationToken);
|
||||
await UiHelper.UpdateModelItems(appDbContext.Configurations, vm.Configurations, model.Configurations, cancellationToken);
|
||||
await UiHelper.UpdateModelItems(appDbContext.V8Files, vm.Files, model.Files, cancellationToken);
|
||||
|
||||
await appDbContext.SaveChangesAsync(cancellationToken);
|
||||
|
||||
@ -190,9 +190,9 @@ public class UpdateInfoBaseTasksController(AppDbContext appDbContext, AgentsConn
|
||||
|
||||
private async Task<UpdateInfoBaseTaskEditViewModel> PrepareViewModel(UpdateInfoBaseTaskEditViewModel vm, CancellationToken cancellationToken)
|
||||
{
|
||||
vm.AvailableConfigurations = await UiHelper.SelectableItemsFrom(
|
||||
appDbContext.Configurations,
|
||||
vm.Configurations,
|
||||
vm.AvailableFiles = await UiHelper.SelectableItemsFrom(
|
||||
appDbContext.V8Files,
|
||||
vm.Files,
|
||||
mapper,
|
||||
cancellationToken);
|
||||
|
||||
|
@ -1,26 +1,29 @@
|
||||
using System.Net.Http.Headers;
|
||||
using System.Runtime.InteropServices.JavaScript;
|
||||
using System.Text.Json;
|
||||
using AutoMapper;
|
||||
using AutoMapper.QueryableExtensions;
|
||||
using Microsoft.AspNetCore.Http.Extensions;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.WebUtilities;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using OnecMonitor.Server;
|
||||
using OnecMonitor.Server.Models;
|
||||
using OnecMonitor.Server.ViewModels;
|
||||
using OnecMonitor.Server.ViewModels.Configurations;
|
||||
using OnecMonitor.Server.ViewModels.V8Files;
|
||||
using OneSTools.Common.Platform.Unpack;
|
||||
|
||||
namespace OnecMonitor.Server.Controllers;
|
||||
|
||||
public class ConfigurationsController(AppDbContext appDbContext, IMapper mapper, IWebHostEnvironment webHostEnvironment) : Controller
|
||||
public class V8FilesController(AppDbContext appDbContext, IMapper mapper, IWebHostEnvironment webHostEnvironment) : Controller
|
||||
{
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
return View(new ConfigurationsIndexViewModel
|
||||
return View(new V8FilesIndexViewModel
|
||||
{
|
||||
Items = await appDbContext.Configurations
|
||||
.ProjectTo<ConfigurationListItemViewModel>(mapper.ConfigurationProvider)
|
||||
Items = await appDbContext.V8Files
|
||||
.ProjectTo<V8FileListItemViewModel>(mapper.ConfigurationProvider)
|
||||
.ToListAsync()
|
||||
});
|
||||
}
|
||||
@ -28,14 +31,14 @@ public class ConfigurationsController(AppDbContext appDbContext, IMapper mapper,
|
||||
public async Task<IActionResult> Edit(Guid id)
|
||||
{
|
||||
if (id == Guid.Empty)
|
||||
return View(new ConfigurationEditViewModel());
|
||||
return View(new V8FileEditViewModel());
|
||||
|
||||
var item = await appDbContext.Configurations.FindAsync(id);
|
||||
var item = await appDbContext.V8Files.FindAsync(id);
|
||||
|
||||
if (item == null)
|
||||
return NotFound();
|
||||
|
||||
return View(new ConfigurationEditViewModel
|
||||
return View(new V8FileEditViewModel
|
||||
{
|
||||
Id = item.Id,
|
||||
Name = item.Name,
|
||||
@ -48,31 +51,46 @@ public class ConfigurationsController(AppDbContext appDbContext, IMapper mapper,
|
||||
MultipartBodyLengthLimit = int.MaxValue,
|
||||
ValueLengthLimit = int.MaxValue)
|
||||
]
|
||||
public async Task<IActionResult> Save(Guid id, ConfigurationEditViewModel vm, CancellationToken cancellationToken)
|
||||
public async Task<IActionResult> Save(Guid id, V8FileEditViewModel vm, CancellationToken cancellationToken)
|
||||
{
|
||||
var isNew = id == Guid.Empty;
|
||||
|
||||
|
||||
if (!isNew)
|
||||
ModelState.Remove(nameof(ConfigurationEditViewModel.File));
|
||||
ModelState.Remove(nameof(V8FileEditViewModel.File));
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
return View("Edit", vm);
|
||||
|
||||
var model = isNew ? new V8Configuration
|
||||
var model = isNew ? new V8File
|
||||
{
|
||||
Id = Guid.NewGuid()
|
||||
} : await appDbContext.Configurations.FindAsync([id], cancellationToken);
|
||||
} : await appDbContext.V8Files.FindAsync([id], cancellationToken);
|
||||
|
||||
if (model == null)
|
||||
return NotFound();
|
||||
|
||||
|
||||
if (isNew)
|
||||
{
|
||||
// save file
|
||||
var extension = Path.GetExtension(vm.File.FileName);
|
||||
|
||||
if (extension.Equals(".CF", StringComparison.InvariantCultureIgnoreCase))
|
||||
model.IsConfiguration = true;
|
||||
else if (extension.Equals(".CFE", StringComparison.InvariantCultureIgnoreCase))
|
||||
model.IsExtension = true;
|
||||
else if (extension.Equals(".CFU", StringComparison.InvariantCultureIgnoreCase))
|
||||
model.IsUpdate = true;
|
||||
else if (extension.Equals(".EPF", StringComparison.InvariantCultureIgnoreCase))
|
||||
model.IsExternalDataProcessor = true;
|
||||
else
|
||||
ModelState.AddModelError(nameof(V8FileEditViewModel.File), "Invalid file format");
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
return View("Edit", vm);
|
||||
|
||||
var fileName = $"{vm.Name}_{vm.Version}{extension}";
|
||||
var path = Path.Combine(webHostEnvironment.ContentRootPath, "Data", fileName);
|
||||
|
||||
|
||||
if (System.IO.File.Exists(path))
|
||||
return View("Error", new ErrorViewModel($"File {path} already exists."));
|
||||
|
||||
@ -81,34 +99,27 @@ public class ConfigurationsController(AppDbContext appDbContext, IMapper mapper,
|
||||
|
||||
model.DataPath = path;
|
||||
|
||||
await appDbContext.Configurations.AddAsync(model, cancellationToken);
|
||||
|
||||
if (extension.Equals(".CF", StringComparison.InvariantCultureIgnoreCase))
|
||||
model.IsConfiguration = true;
|
||||
else if (extension.Equals(".CFE", StringComparison.InvariantCultureIgnoreCase))
|
||||
model.IsExtension = true;
|
||||
else if (extension.Equals(".CFU", StringComparison.InvariantCultureIgnoreCase))
|
||||
model.IsUpdate = true;
|
||||
await appDbContext.V8Files.AddAsync(model, cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
model.Name = vm.Name;
|
||||
model.Version = vm.Version;
|
||||
|
||||
|
||||
await appDbContext.SaveChangesAsync(cancellationToken);
|
||||
|
||||
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Delete(Guid id, CancellationToken cancellationToken)
|
||||
{
|
||||
var item = await appDbContext.Configurations.FindAsync(
|
||||
var item = await appDbContext.V8Files.FindAsync(
|
||||
[id],
|
||||
cancellationToken: cancellationToken);
|
||||
|
||||
if (System.IO.File.Exists(item!.DataPath))
|
||||
System.IO.File.Delete(item.DataPath);
|
||||
|
||||
appDbContext.Configurations.Remove(item!);
|
||||
appDbContext.V8Files.Remove(item!);
|
||||
await appDbContext.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return RedirectToAction("Index");
|
17
onecmonitor-server/Extensions/EnumExtension.cs
Normal file
17
onecmonitor-server/Extensions/EnumExtension.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace OnecMonitor.Server.Extensions;
|
||||
|
||||
public static class EnumExtension
|
||||
{
|
||||
public static string GetDisplay(this Enum value)
|
||||
=> value.GetAttributeOfType<DisplayAttribute>()?.Name ?? value.ToString();
|
||||
|
||||
private static T? GetAttributeOfType<T>(this Enum enumVal) where T : Attribute
|
||||
{
|
||||
var type = enumVal.GetType();
|
||||
var memInfo = type.GetMember(enumVal.ToString());
|
||||
var attributes = memInfo[0].GetCustomAttributes(typeof(T), false);
|
||||
return (attributes.Length > 0) ? (T)attributes[0] : null;
|
||||
}
|
||||
}
|
@ -9,7 +9,7 @@ namespace OnecMonitor.Server.Helpers;
|
||||
|
||||
public static class UiHelper
|
||||
{
|
||||
public static SelectList SelectListFromEnum<T1>() where T1 : struct, Enum
|
||||
public static SelectList SelectListFromEnum<T1>(T1? selectedValue = null) where T1 : struct, Enum
|
||||
{
|
||||
var values = Enum.GetValues<T1>().ToList();
|
||||
var selectListItems = values.Select(i => new { Id = i.ToString(), Name = i.GetAttributeOfType<DisplayAttribute>()?.Name ?? i.ToString() }).ToList();
|
||||
@ -18,7 +18,8 @@ public static class UiHelper
|
||||
return new SelectList(
|
||||
selectListItems,
|
||||
"Id",
|
||||
"Name");
|
||||
"Name",
|
||||
selectedValue == null ? "" : selectedValue);
|
||||
}
|
||||
|
||||
public static async Task<SelectList> SelectListFrom<T1>(
|
||||
|
@ -10,7 +10,7 @@ using OnecMonitor.Server;
|
||||
namespace OnecMonitor.Server.Migrations
|
||||
{
|
||||
[DbContext(typeof(AppDbContext))]
|
||||
[Migration("20250214151907_Initial")]
|
||||
[Migration("20250225183414_Initial")]
|
||||
partial class Initial
|
||||
{
|
||||
/// <inheritdoc />
|
||||
@ -170,6 +170,9 @@ namespace OnecMonitor.Server.Migrations
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("MaintenanceTaskId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
@ -184,6 +187,8 @@ namespace OnecMonitor.Server.Migrations
|
||||
|
||||
b.HasIndex("CredentialsId");
|
||||
|
||||
b.HasIndex("MaintenanceTaskId");
|
||||
|
||||
b.ToTable("InfoBases");
|
||||
});
|
||||
|
||||
@ -206,6 +211,87 @@ namespace OnecMonitor.Server.Migrations
|
||||
b.ToTable("LogTemplates");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("OnecMonitor.Server.Models.MaintenanceTasks.MaintenanceStep", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("AccessCode")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("FileId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Kind")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Message")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("FileId");
|
||||
|
||||
b.ToTable("MaintenanceStep");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("OnecMonitor.Server.Models.MaintenanceTasks.MaintenanceStepNode", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Kind")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("LeftNodeId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RightNodeId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("StepId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("LeftNodeId");
|
||||
|
||||
b.HasIndex("RightNodeId");
|
||||
|
||||
b.HasIndex("StepId");
|
||||
|
||||
b.ToTable("MaintenanceStepNodes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("OnecMonitor.Server.Models.MaintenanceTasks.MaintenanceTask", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RootNodeId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RootNodeId");
|
||||
|
||||
b.ToTable("MaintenanceTasks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("OnecMonitor.Server.Models.TechLogFilter", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
@ -344,7 +430,7 @@ namespace OnecMonitor.Server.Migrations
|
||||
b.ToTable("UpdateInfoBaseTaskLogItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("OnecMonitor.Server.Models.V8Configuration", b =>
|
||||
modelBuilder.Entity("OnecMonitor.Server.Models.V8File", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
@ -360,6 +446,9 @@ namespace OnecMonitor.Server.Migrations
|
||||
b.Property<bool>("IsExtension")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsExternalDataProcessor")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsUpdate")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
@ -373,22 +462,22 @@ namespace OnecMonitor.Server.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Configurations");
|
||||
b.ToTable("V8Files");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("UpdateInfoBaseTaskV8Configuration", b =>
|
||||
modelBuilder.Entity("UpdateInfoBaseTaskV8File", b =>
|
||||
{
|
||||
b.Property<string>("ConfigurationsId")
|
||||
b.Property<string>("FilesId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("UpdateTasksId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("ConfigurationsId", "UpdateTasksId");
|
||||
b.HasKey("FilesId", "UpdateTasksId");
|
||||
|
||||
b.HasIndex("UpdateTasksId");
|
||||
|
||||
b.ToTable("UpdateInfoBaseTaskV8Configuration");
|
||||
b.ToTable("UpdateInfoBaseTaskV8File");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AgentTechLogSeance", b =>
|
||||
@ -467,11 +556,58 @@ namespace OnecMonitor.Server.Migrations
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("OnecMonitor.Server.Models.MaintenanceTasks.MaintenanceTask", null)
|
||||
.WithMany("InfoBases")
|
||||
.HasForeignKey("MaintenanceTaskId");
|
||||
|
||||
b.Navigation("Cluster");
|
||||
|
||||
b.Navigation("Credentials");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("OnecMonitor.Server.Models.MaintenanceTasks.MaintenanceStep", b =>
|
||||
{
|
||||
b.HasOne("OnecMonitor.Server.Models.V8File", "File")
|
||||
.WithMany()
|
||||
.HasForeignKey("FileId");
|
||||
|
||||
b.Navigation("File");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("OnecMonitor.Server.Models.MaintenanceTasks.MaintenanceStepNode", b =>
|
||||
{
|
||||
b.HasOne("OnecMonitor.Server.Models.MaintenanceTasks.MaintenanceStepNode", "LeftNode")
|
||||
.WithMany()
|
||||
.HasForeignKey("LeftNodeId");
|
||||
|
||||
b.HasOne("OnecMonitor.Server.Models.MaintenanceTasks.MaintenanceStepNode", "RightNode")
|
||||
.WithMany()
|
||||
.HasForeignKey("RightNodeId");
|
||||
|
||||
b.HasOne("OnecMonitor.Server.Models.MaintenanceTasks.MaintenanceStep", "Step")
|
||||
.WithMany()
|
||||
.HasForeignKey("StepId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("LeftNode");
|
||||
|
||||
b.Navigation("RightNode");
|
||||
|
||||
b.Navigation("Step");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("OnecMonitor.Server.Models.MaintenanceTasks.MaintenanceTask", b =>
|
||||
{
|
||||
b.HasOne("OnecMonitor.Server.Models.MaintenanceTasks.MaintenanceStepNode", "RootNode")
|
||||
.WithMany()
|
||||
.HasForeignKey("RootNodeId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("RootNode");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("OnecMonitor.Server.Models.UpdateInfoBaseTaskLogItem", b =>
|
||||
{
|
||||
b.HasOne("OnecMonitor.Server.Models.InfoBase", "InfoBase")
|
||||
@ -491,11 +627,11 @@ namespace OnecMonitor.Server.Migrations
|
||||
b.Navigation("Task");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("UpdateInfoBaseTaskV8Configuration", b =>
|
||||
modelBuilder.Entity("UpdateInfoBaseTaskV8File", b =>
|
||||
{
|
||||
b.HasOne("OnecMonitor.Server.Models.V8Configuration", null)
|
||||
b.HasOne("OnecMonitor.Server.Models.V8File", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("ConfigurationsId")
|
||||
.HasForeignKey("FilesId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
@ -523,6 +659,11 @@ namespace OnecMonitor.Server.Migrations
|
||||
b.Navigation("InfoBases");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("OnecMonitor.Server.Models.MaintenanceTasks.MaintenanceTask", b =>
|
||||
{
|
||||
b.Navigation("InfoBases");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("OnecMonitor.Server.Models.UpdateInfoBaseTask", b =>
|
||||
{
|
||||
b.Navigation("Log");
|
@ -22,23 +22,6 @@ namespace OnecMonitor.Server.Migrations
|
||||
table.PrimaryKey("PK_Agents", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Configurations",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(type: "TEXT", nullable: false),
|
||||
Name = table.Column<string>(type: "TEXT", nullable: false),
|
||||
Version = table.Column<string>(type: "TEXT", nullable: false),
|
||||
DataPath = table.Column<string>(type: "TEXT", nullable: false),
|
||||
IsUpdate = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
IsExtension = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
IsConfiguration = table.Column<bool>(type: "INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Configurations", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Credentials",
|
||||
columns: table => new
|
||||
@ -127,6 +110,24 @@ namespace OnecMonitor.Server.Migrations
|
||||
table.PrimaryKey("PK_UpdateInfoBaseTasks", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "V8Files",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(type: "TEXT", nullable: false),
|
||||
Name = table.Column<string>(type: "TEXT", nullable: false),
|
||||
Version = table.Column<string>(type: "TEXT", nullable: false),
|
||||
DataPath = table.Column<string>(type: "TEXT", nullable: false),
|
||||
IsUpdate = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
IsExtension = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
IsConfiguration = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
IsExternalDataProcessor = table.Column<bool>(type: "INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_V8Files", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Clusters",
|
||||
columns: table => new
|
||||
@ -204,25 +205,95 @@ namespace OnecMonitor.Server.Migrations
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "UpdateInfoBaseTaskV8Configuration",
|
||||
name: "MaintenanceStep",
|
||||
columns: table => new
|
||||
{
|
||||
ConfigurationsId = table.Column<string>(type: "TEXT", nullable: false),
|
||||
Id = table.Column<string>(type: "TEXT", nullable: false),
|
||||
Kind = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
AccessCode = table.Column<string>(type: "TEXT", maxLength: 20, nullable: false),
|
||||
Message = table.Column<string>(type: "TEXT", maxLength: 200, nullable: false),
|
||||
FileId = table.Column<string>(type: "TEXT", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_MaintenanceStep", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_MaintenanceStep_V8Files_FileId",
|
||||
column: x => x.FileId,
|
||||
principalTable: "V8Files",
|
||||
principalColumn: "Id");
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "UpdateInfoBaseTaskV8File",
|
||||
columns: table => new
|
||||
{
|
||||
FilesId = table.Column<string>(type: "TEXT", nullable: false),
|
||||
UpdateTasksId = table.Column<string>(type: "TEXT", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_UpdateInfoBaseTaskV8Configuration", x => new { x.ConfigurationsId, x.UpdateTasksId });
|
||||
table.PrimaryKey("PK_UpdateInfoBaseTaskV8File", x => new { x.FilesId, x.UpdateTasksId });
|
||||
table.ForeignKey(
|
||||
name: "FK_UpdateInfoBaseTaskV8Configuration_Configurations_ConfigurationsId",
|
||||
column: x => x.ConfigurationsId,
|
||||
principalTable: "Configurations",
|
||||
name: "FK_UpdateInfoBaseTaskV8File_UpdateInfoBaseTasks_UpdateTasksId",
|
||||
column: x => x.UpdateTasksId,
|
||||
principalTable: "UpdateInfoBaseTasks",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_UpdateInfoBaseTaskV8Configuration_UpdateInfoBaseTasks_UpdateTasksId",
|
||||
column: x => x.UpdateTasksId,
|
||||
principalTable: "UpdateInfoBaseTasks",
|
||||
name: "FK_UpdateInfoBaseTaskV8File_V8Files_FilesId",
|
||||
column: x => x.FilesId,
|
||||
principalTable: "V8Files",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "MaintenanceStepNodes",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(type: "TEXT", nullable: false),
|
||||
Kind = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
LeftNodeId = table.Column<string>(type: "TEXT", nullable: true),
|
||||
RightNodeId = table.Column<string>(type: "TEXT", nullable: true),
|
||||
StepId = table.Column<string>(type: "TEXT", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_MaintenanceStepNodes", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_MaintenanceStepNodes_MaintenanceStepNodes_LeftNodeId",
|
||||
column: x => x.LeftNodeId,
|
||||
principalTable: "MaintenanceStepNodes",
|
||||
principalColumn: "Id");
|
||||
table.ForeignKey(
|
||||
name: "FK_MaintenanceStepNodes_MaintenanceStepNodes_RightNodeId",
|
||||
column: x => x.RightNodeId,
|
||||
principalTable: "MaintenanceStepNodes",
|
||||
principalColumn: "Id");
|
||||
table.ForeignKey(
|
||||
name: "FK_MaintenanceStepNodes_MaintenanceStep_StepId",
|
||||
column: x => x.StepId,
|
||||
principalTable: "MaintenanceStep",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "MaintenanceTasks",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(type: "TEXT", nullable: false),
|
||||
Description = table.Column<string>(type: "TEXT", maxLength: 200, nullable: false),
|
||||
RootNodeId = table.Column<string>(type: "TEXT", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_MaintenanceTasks", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_MaintenanceTasks_MaintenanceStepNodes_RootNodeId",
|
||||
column: x => x.RootNodeId,
|
||||
principalTable: "MaintenanceStepNodes",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
@ -237,7 +308,8 @@ namespace OnecMonitor.Server.Migrations
|
||||
InfoBaseName = table.Column<string>(type: "TEXT", nullable: false),
|
||||
PublishAddress = table.Column<string>(type: "TEXT", nullable: false),
|
||||
CredentialsId = table.Column<string>(type: "TEXT", nullable: false),
|
||||
ClusterId = table.Column<string>(type: "TEXT", nullable: false)
|
||||
ClusterId = table.Column<string>(type: "TEXT", nullable: false),
|
||||
MaintenanceTaskId = table.Column<string>(type: "TEXT", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@ -254,6 +326,11 @@ namespace OnecMonitor.Server.Migrations
|
||||
principalTable: "Credentials",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_InfoBases_MaintenanceTasks_MaintenanceTaskId",
|
||||
column: x => x.MaintenanceTaskId,
|
||||
principalTable: "MaintenanceTasks",
|
||||
principalColumn: "Id");
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
@ -334,6 +411,11 @@ namespace OnecMonitor.Server.Migrations
|
||||
table: "InfoBases",
|
||||
column: "CredentialsId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_InfoBases_MaintenanceTaskId",
|
||||
table: "InfoBases",
|
||||
column: "MaintenanceTaskId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_InfoBaseUpdateInfoBaseTask_UpdateTasksId",
|
||||
table: "InfoBaseUpdateInfoBaseTask",
|
||||
@ -344,6 +426,31 @@ namespace OnecMonitor.Server.Migrations
|
||||
table: "LogTemplateTechLogSeance",
|
||||
column: "TemplatesId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_MaintenanceStep_FileId",
|
||||
table: "MaintenanceStep",
|
||||
column: "FileId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_MaintenanceStepNodes_LeftNodeId",
|
||||
table: "MaintenanceStepNodes",
|
||||
column: "LeftNodeId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_MaintenanceStepNodes_RightNodeId",
|
||||
table: "MaintenanceStepNodes",
|
||||
column: "RightNodeId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_MaintenanceStepNodes_StepId",
|
||||
table: "MaintenanceStepNodes",
|
||||
column: "StepId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_MaintenanceTasks_RootNodeId",
|
||||
table: "MaintenanceTasks",
|
||||
column: "RootNodeId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_UpdateInfoBaseTaskLogItems_InfoBaseId",
|
||||
table: "UpdateInfoBaseTaskLogItems",
|
||||
@ -355,8 +462,8 @@ namespace OnecMonitor.Server.Migrations
|
||||
column: "TaskId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_UpdateInfoBaseTaskV8Configuration_UpdateTasksId",
|
||||
table: "UpdateInfoBaseTaskV8Configuration",
|
||||
name: "IX_UpdateInfoBaseTaskV8File_UpdateTasksId",
|
||||
table: "UpdateInfoBaseTaskV8File",
|
||||
column: "UpdateTasksId");
|
||||
}
|
||||
|
||||
@ -382,7 +489,7 @@ namespace OnecMonitor.Server.Migrations
|
||||
name: "UpdateInfoBaseTaskLogItems");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "UpdateInfoBaseTaskV8Configuration");
|
||||
name: "UpdateInfoBaseTaskV8File");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "LogTemplates");
|
||||
@ -393,20 +500,29 @@ namespace OnecMonitor.Server.Migrations
|
||||
migrationBuilder.DropTable(
|
||||
name: "InfoBases");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Configurations");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "UpdateInfoBaseTasks");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Clusters");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "MaintenanceTasks");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Agents");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Credentials");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "MaintenanceStepNodes");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "MaintenanceStep");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "V8Files");
|
||||
}
|
||||
}
|
||||
}
|
@ -167,6 +167,9 @@ namespace OnecMonitor.Server.Migrations
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("MaintenanceTaskId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
@ -181,6 +184,8 @@ namespace OnecMonitor.Server.Migrations
|
||||
|
||||
b.HasIndex("CredentialsId");
|
||||
|
||||
b.HasIndex("MaintenanceTaskId");
|
||||
|
||||
b.ToTable("InfoBases");
|
||||
});
|
||||
|
||||
@ -203,6 +208,87 @@ namespace OnecMonitor.Server.Migrations
|
||||
b.ToTable("LogTemplates");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("OnecMonitor.Server.Models.MaintenanceTasks.MaintenanceStep", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("AccessCode")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("FileId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Kind")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Message")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("FileId");
|
||||
|
||||
b.ToTable("MaintenanceStep");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("OnecMonitor.Server.Models.MaintenanceTasks.MaintenanceStepNode", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Kind")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("LeftNodeId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RightNodeId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("StepId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("LeftNodeId");
|
||||
|
||||
b.HasIndex("RightNodeId");
|
||||
|
||||
b.HasIndex("StepId");
|
||||
|
||||
b.ToTable("MaintenanceStepNodes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("OnecMonitor.Server.Models.MaintenanceTasks.MaintenanceTask", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RootNodeId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RootNodeId");
|
||||
|
||||
b.ToTable("MaintenanceTasks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("OnecMonitor.Server.Models.TechLogFilter", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
@ -341,7 +427,7 @@ namespace OnecMonitor.Server.Migrations
|
||||
b.ToTable("UpdateInfoBaseTaskLogItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("OnecMonitor.Server.Models.V8Configuration", b =>
|
||||
modelBuilder.Entity("OnecMonitor.Server.Models.V8File", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
@ -357,6 +443,9 @@ namespace OnecMonitor.Server.Migrations
|
||||
b.Property<bool>("IsExtension")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsExternalDataProcessor")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsUpdate")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
@ -370,22 +459,22 @@ namespace OnecMonitor.Server.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Configurations");
|
||||
b.ToTable("V8Files");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("UpdateInfoBaseTaskV8Configuration", b =>
|
||||
modelBuilder.Entity("UpdateInfoBaseTaskV8File", b =>
|
||||
{
|
||||
b.Property<string>("ConfigurationsId")
|
||||
b.Property<string>("FilesId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("UpdateTasksId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("ConfigurationsId", "UpdateTasksId");
|
||||
b.HasKey("FilesId", "UpdateTasksId");
|
||||
|
||||
b.HasIndex("UpdateTasksId");
|
||||
|
||||
b.ToTable("UpdateInfoBaseTaskV8Configuration");
|
||||
b.ToTable("UpdateInfoBaseTaskV8File");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AgentTechLogSeance", b =>
|
||||
@ -464,11 +553,58 @@ namespace OnecMonitor.Server.Migrations
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("OnecMonitor.Server.Models.MaintenanceTasks.MaintenanceTask", null)
|
||||
.WithMany("InfoBases")
|
||||
.HasForeignKey("MaintenanceTaskId");
|
||||
|
||||
b.Navigation("Cluster");
|
||||
|
||||
b.Navigation("Credentials");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("OnecMonitor.Server.Models.MaintenanceTasks.MaintenanceStep", b =>
|
||||
{
|
||||
b.HasOne("OnecMonitor.Server.Models.V8File", "File")
|
||||
.WithMany()
|
||||
.HasForeignKey("FileId");
|
||||
|
||||
b.Navigation("File");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("OnecMonitor.Server.Models.MaintenanceTasks.MaintenanceStepNode", b =>
|
||||
{
|
||||
b.HasOne("OnecMonitor.Server.Models.MaintenanceTasks.MaintenanceStepNode", "LeftNode")
|
||||
.WithMany()
|
||||
.HasForeignKey("LeftNodeId");
|
||||
|
||||
b.HasOne("OnecMonitor.Server.Models.MaintenanceTasks.MaintenanceStepNode", "RightNode")
|
||||
.WithMany()
|
||||
.HasForeignKey("RightNodeId");
|
||||
|
||||
b.HasOne("OnecMonitor.Server.Models.MaintenanceTasks.MaintenanceStep", "Step")
|
||||
.WithMany()
|
||||
.HasForeignKey("StepId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("LeftNode");
|
||||
|
||||
b.Navigation("RightNode");
|
||||
|
||||
b.Navigation("Step");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("OnecMonitor.Server.Models.MaintenanceTasks.MaintenanceTask", b =>
|
||||
{
|
||||
b.HasOne("OnecMonitor.Server.Models.MaintenanceTasks.MaintenanceStepNode", "RootNode")
|
||||
.WithMany()
|
||||
.HasForeignKey("RootNodeId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("RootNode");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("OnecMonitor.Server.Models.UpdateInfoBaseTaskLogItem", b =>
|
||||
{
|
||||
b.HasOne("OnecMonitor.Server.Models.InfoBase", "InfoBase")
|
||||
@ -488,11 +624,11 @@ namespace OnecMonitor.Server.Migrations
|
||||
b.Navigation("Task");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("UpdateInfoBaseTaskV8Configuration", b =>
|
||||
modelBuilder.Entity("UpdateInfoBaseTaskV8File", b =>
|
||||
{
|
||||
b.HasOne("OnecMonitor.Server.Models.V8Configuration", null)
|
||||
b.HasOne("OnecMonitor.Server.Models.V8File", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("ConfigurationsId")
|
||||
.HasForeignKey("FilesId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
@ -520,6 +656,11 @@ namespace OnecMonitor.Server.Migrations
|
||||
b.Navigation("InfoBases");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("OnecMonitor.Server.Models.MaintenanceTasks.MaintenanceTask", b =>
|
||||
{
|
||||
b.Navigation("InfoBases");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("OnecMonitor.Server.Models.UpdateInfoBaseTask", b =>
|
||||
{
|
||||
b.Navigation("Log");
|
||||
|
@ -1,11 +0,0 @@
|
||||
using System.Configuration;
|
||||
|
||||
namespace OnecMonitor.Server.Models;
|
||||
|
||||
public abstract class MaintenanceStep
|
||||
{
|
||||
public MaintenanceStepKind Kind { get; set; }
|
||||
public Configuration? Configuration { get; set; }
|
||||
public string AccessCode { get; set; } = string.Empty;
|
||||
public string Message { get; set; } = string.Empty;
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace OnecMonitor.Server.Models.MaintenanceTasks;
|
||||
|
||||
public class MaintenanceStep : DatabaseObject
|
||||
{
|
||||
public MaintenanceStepKind Kind { get; set; }
|
||||
|
||||
[MaxLength(20)]
|
||||
public string AccessCode { get; set; } = string.Empty;
|
||||
[MaxLength(200)]
|
||||
public string Message { get; set; } = string.Empty;
|
||||
public Guid? FileId { get; set; }
|
||||
|
||||
public V8File? File { get; set; } = null!;
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace OnecMonitor.Server.Models;
|
||||
namespace OnecMonitor.Server.Models.MaintenanceTasks;
|
||||
|
||||
public enum MaintenanceStepKind
|
||||
{
|
@ -0,0 +1,19 @@
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace OnecMonitor.Server.Models.MaintenanceTasks;
|
||||
|
||||
public class MaintenanceStepNode : DatabaseObject
|
||||
{
|
||||
public MaintenanceStepNodeKind Kind { get; set; }
|
||||
|
||||
public Guid? LeftNodeId { get; set; }
|
||||
public Guid? RightNodeId { get; set; }
|
||||
public Guid StepId { get; set; }
|
||||
|
||||
[ForeignKey(nameof(LeftNodeId))]
|
||||
public MaintenanceStepNode LeftNode { get; set; } = null!;
|
||||
[ForeignKey(nameof(RightNodeId))]
|
||||
public MaintenanceStepNode RightNode { get; set; } = null!;
|
||||
[ForeignKey(nameof(StepId))]
|
||||
public MaintenanceStep Step { get; set; } = null!;
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace OnecMonitor.Server.Models.MaintenanceTasks;
|
||||
|
||||
public enum MaintenanceStepNodeKind
|
||||
{
|
||||
[Display(Name = "Простой")]
|
||||
Simple,
|
||||
[Display(Name = "Попытка/Исключение")]
|
||||
TryCatch
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace OnecMonitor.Server.Models.MaintenanceTasks;
|
||||
|
||||
public class MaintenanceTask : DatabaseObject
|
||||
{
|
||||
[MaxLength(200)]
|
||||
public string Description { get; set; } = string.Empty;
|
||||
public Guid RootNodeId { get; set; }
|
||||
|
||||
[ForeignKey(nameof(RootNodeId))]
|
||||
public MaintenanceStepNode RootNode { get; set; } = null!;
|
||||
|
||||
public virtual List<InfoBase> InfoBases { get; set; } = [];
|
||||
}
|
@ -12,7 +12,7 @@ public class UpdateInfoBaseTask : DatabaseObject
|
||||
public string Description { get; set; } = string.Empty;
|
||||
public DateTime StartDateTime { get; set; } = DateTime.MinValue;
|
||||
|
||||
public virtual List<V8Configuration> Configurations { get; set; } = [];
|
||||
public virtual List<V8File> Files { get; set; } = [];
|
||||
public virtual List<InfoBase> InfoBases { get; set; } = [];
|
||||
public virtual List<UpdateInfoBaseTaskLogItem> Log { get; set; } = [];
|
||||
}
|
@ -2,7 +2,7 @@ using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace OnecMonitor.Server.Models;
|
||||
|
||||
public class V8Configuration : DatabaseObject
|
||||
public class V8File : DatabaseObject
|
||||
{
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string Version { get; set; } = string.Empty;
|
||||
@ -10,6 +10,7 @@ public class V8Configuration : DatabaseObject
|
||||
public bool IsUpdate { get; set; } = false;
|
||||
public bool IsExtension { get; set; } = false;
|
||||
public bool IsConfiguration { get; set; } = false;
|
||||
public bool IsExternalDataProcessor { get; set; } = false;
|
||||
|
||||
public virtual List<UpdateInfoBaseTask> UpdateTasks { get; set; } = [];
|
||||
|
||||
@ -23,6 +24,8 @@ public class V8Configuration : DatabaseObject
|
||||
postfix = "расширение";
|
||||
else if (IsConfiguration)
|
||||
postfix = "конфигурация";
|
||||
else if (IsExternalDataProcessor)
|
||||
postfix = "внешняя обработка";
|
||||
|
||||
return $"{Name} ({Version}, {postfix})";
|
||||
}
|
@ -14,10 +14,13 @@ using OnecMonitor.Server.AutoMapper;
|
||||
using OnecMonitor.Server.Models;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Services.AddWindowsService(options =>
|
||||
{
|
||||
options.ServiceName = "OnecMonitor";
|
||||
});
|
||||
builder.Services.AddSystemd();
|
||||
|
||||
builder.WebHost.ConfigureKestrel((context, options) =>
|
||||
{
|
||||
options.Limits.MaxRequestBodySize = 2000 * 1024 * 1024;
|
||||
|
122
onecmonitor-server/Scripts/MaintenanceTask/editStepDialog.ts
Normal file
122
onecmonitor-server/Scripts/MaintenanceTask/editStepDialog.ts
Normal file
@ -0,0 +1,122 @@
|
||||
import * as bootstrap from "bootstrap";
|
||||
import {Modal} from "bootstrap";
|
||||
import * as uuid from "uuid";
|
||||
import { StepValidationResult } from './stepValidationResult'
|
||||
|
||||
export class EditStepDialog {
|
||||
private dialogElement: HTMLDivElement;
|
||||
private dialogBody: HTMLDivElement;
|
||||
private modal: Modal;
|
||||
|
||||
private onSaveCallback: (step: any) => void = undefined;
|
||||
|
||||
constructor(element: HTMLDivElement) {
|
||||
this.dialogElement = element;
|
||||
this.modal = new bootstrap.Modal(element);
|
||||
this.dialogBody = this.dialogElement.querySelector('.modal-body');
|
||||
|
||||
this.dialogElement.querySelector<HTMLButtonElement>('#save-btn').onclick = async () => {
|
||||
try {
|
||||
const result = await this.validateStepForm();
|
||||
|
||||
if (result.isValid) {
|
||||
this.close();
|
||||
this.onSaveCallback(result.payload);
|
||||
} else
|
||||
this.dialogBody.innerHTML = result.payload;
|
||||
} catch (e) {
|
||||
alert(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public open(onSave: (step: any) => void, step: any | undefined = undefined) {
|
||||
this.onSaveCallback = onSave;
|
||||
|
||||
if (step == undefined)
|
||||
step = {
|
||||
Id: uuid.v4()
|
||||
};
|
||||
|
||||
this.getEditStepForm(step).then(data => {
|
||||
this.dialogBody.innerHTML = data;
|
||||
|
||||
const select = this.dialogBody.querySelector<HTMLSelectElement>("select[name='Kind']");
|
||||
select.onchange = async () => {
|
||||
await this.updateStepForm();
|
||||
};
|
||||
|
||||
this.modal.show();
|
||||
});
|
||||
}
|
||||
|
||||
public close(): void {
|
||||
this.modal.hide();
|
||||
}
|
||||
|
||||
private async updateStepForm() {
|
||||
const formData = new FormData(this.dialogBody.querySelector<HTMLFormElement>('form'));
|
||||
|
||||
const response = await fetch('/MaintenanceTasks/UpdateStep', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
this.dialogBody.innerHTML = await response.text();
|
||||
|
||||
const select = this.dialogBody.querySelector<HTMLSelectElement>("select[name='Kind']");
|
||||
select.onchange = async () => {
|
||||
await this.updateStepForm();
|
||||
};
|
||||
} else
|
||||
alert(`Failed to update step form: ${response.statusText}`);
|
||||
}
|
||||
|
||||
private async getEditStepForm(step: any | undefined = undefined): Promise<string> {
|
||||
return new Promise<string | undefined>(async (resolve, reject) => {
|
||||
const body = step != undefined ? JSON.stringify(step) : "";
|
||||
|
||||
const response = await fetch("/MaintenanceTasks/EditStep", {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: body,
|
||||
});
|
||||
|
||||
if (response.ok)
|
||||
resolve(await response.text());
|
||||
else
|
||||
reject(`Failed to get step kind view: ${response.statusText}`);
|
||||
});
|
||||
}
|
||||
|
||||
private async validateStepForm(): Promise<StepValidationResult> {
|
||||
return new Promise<StepValidationResult>(async (resolve, reject) => {
|
||||
const formData = new FormData(this.dialogBody.querySelector<HTMLFormElement>('form'));
|
||||
|
||||
const validateResponse = await fetch('/MaintenanceTasks/ValidateStep', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
|
||||
if (validateResponse.status == 200) {
|
||||
//editStepModal.hide()
|
||||
|
||||
const result = new StepValidationResult();
|
||||
result.isValid = true;
|
||||
result.payload = await validateResponse.json();
|
||||
|
||||
resolve(result);
|
||||
} else if (validateResponse.status == 400) {
|
||||
const result = new StepValidationResult();
|
||||
result.isValid = false;
|
||||
result.payload = await validateResponse.text();
|
||||
|
||||
resolve(result);
|
||||
} else
|
||||
reject(`Failed to validate step: ${validateResponse.statusText}`);
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
export enum MaintenanceStepNodeKind {
|
||||
Simple,
|
||||
TryCatch
|
||||
}
|
32
onecmonitor-server/Scripts/MaintenanceTask/nodesGraph.ts
Normal file
32
onecmonitor-server/Scripts/MaintenanceTask/nodesGraph.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import {EditStepDialog} from "./editStepDialog";
|
||||
import {NodesGraphOptions} from "./nodesGraphOptions";
|
||||
import {NodesGraphActionsDialog} from './nodesGraphActionsDialog'
|
||||
import {StepsEditorHelper} from "./stepsEditorHelper";
|
||||
|
||||
export class NodesGraph {
|
||||
private nodesGraphActionsDialog: NodesGraphActionsDialog
|
||||
private editStepDialog: EditStepDialog
|
||||
private readonly stepsEditorHelper: StepsEditorHelper
|
||||
|
||||
constructor(options: NodesGraphOptions) {
|
||||
|
||||
this.stepsEditorHelper = new StepsEditorHelper(options.nodesInputElement);
|
||||
this.stepsEditorHelper.init().then(() => {
|
||||
this.editStepDialog = new EditStepDialog(options.editStepModalElement);
|
||||
this.nodesGraphActionsDialog = new NodesGraphActionsDialog(
|
||||
this.editStepDialog,
|
||||
this.stepsEditorHelper,
|
||||
options.nodeGraphActionsElement
|
||||
);
|
||||
|
||||
options.bodyElement.addEventListener('click', async e => {
|
||||
if (options.nodesInputElement.value == undefined || options.nodesInputElement.value == "")
|
||||
this.nodesGraphActionsDialog.show();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public handleNodeClick(nodeId: string) {
|
||||
this.nodesGraphActionsDialog.show(nodeId);
|
||||
}
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
import {EditStepDialog} from "./editStepDialog";
|
||||
import * as uuid from "uuid";
|
||||
import {MaintenanceStepNodeKind} from "./maintenanceStepNodeKind";
|
||||
import {StepsEditorHelper} from './stepsEditorHelper'
|
||||
|
||||
export class NodesGraphActionsDialog {
|
||||
private editStepDialog: EditStepDialog;
|
||||
private stepsEditorHelper: StepsEditorHelper;
|
||||
private dialogElement: HTMLDivElement;
|
||||
|
||||
constructor(editStepDialog: EditStepDialog, stepsEditorHelper: StepsEditorHelper, popupElement: HTMLDivElement) {
|
||||
this.editStepDialog = editStepDialog;
|
||||
this.stepsEditorHelper = stepsEditorHelper;
|
||||
this.dialogElement = popupElement;
|
||||
}
|
||||
|
||||
public show(nodeId: string | undefined = undefined): void {
|
||||
const root = this.stepsEditorHelper.getRootNode();
|
||||
const node = nodeId == undefined ? undefined : this.stepsEditorHelper.findNode(nodeId, root);
|
||||
|
||||
const editBtn = this.dialogElement.querySelector<HTMLButtonElement>('#edit-step-btn');
|
||||
editBtn.hidden = nodeId == undefined;
|
||||
editBtn.onclick = async () => {
|
||||
this.dialogElement.hidePopover();
|
||||
this.editStepDialog.open(step => {
|
||||
node.Step = step;
|
||||
node.StepId = step.id;
|
||||
}, node.Step);
|
||||
}
|
||||
|
||||
const deleteStepBtn = this.dialogElement.querySelector<HTMLButtonElement>('#delete-step-btn');
|
||||
deleteStepBtn.hidden = nodeId == undefined;
|
||||
deleteStepBtn.onclick = async () => {
|
||||
this.dialogElement.hidePopover();
|
||||
|
||||
if (root.Id == node.Id)
|
||||
this.stepsEditorHelper.saveNode(undefined);
|
||||
else {
|
||||
this.stepsEditorHelper.deleteNode(root, node);
|
||||
this.stepsEditorHelper.saveNode(root);
|
||||
}
|
||||
|
||||
await this.stepsEditorHelper.redrawNodes();
|
||||
}
|
||||
|
||||
this.dialogElement.querySelector<HTMLButtonElement>('#add-step-btn').onclick = () => {
|
||||
this.dialogElement.hidePopover();
|
||||
this.addStep(MaintenanceStepNodeKind.Simple, nodeId);
|
||||
}
|
||||
|
||||
this.dialogElement.querySelector<HTMLButtonElement>('#add-binary-step-btn').onclick = () => {
|
||||
this.dialogElement.hidePopover();
|
||||
this.addStep(MaintenanceStepNodeKind.TryCatch, nodeId);
|
||||
}
|
||||
|
||||
const addErrorStepBtn = this.dialogElement.querySelector<HTMLButtonElement>('#add-error-step-btn');
|
||||
addErrorStepBtn.hidden = nodeId == undefined || node.Kind == MaintenanceStepNodeKind.Simple;
|
||||
addErrorStepBtn.onclick = async () => {
|
||||
this.dialogElement.hidePopover();
|
||||
this.addCatchNode(root, node, MaintenanceStepNodeKind.Simple);
|
||||
}
|
||||
|
||||
const addErrorBinaryStepBtn = this.dialogElement.querySelector<HTMLButtonElement>('#add-error-binary-step-btn');
|
||||
addErrorBinaryStepBtn.hidden = nodeId == undefined || node.Kind == MaintenanceStepNodeKind.Simple;
|
||||
addErrorBinaryStepBtn.onclick = async () => {
|
||||
this.dialogElement.hidePopover();
|
||||
this.addCatchNode(root, node, MaintenanceStepNodeKind.TryCatch);
|
||||
}
|
||||
|
||||
this.dialogElement.showPopover()
|
||||
}
|
||||
|
||||
private addStep(kind: MaintenanceStepNodeKind, nodeId: string | undefined = undefined) {
|
||||
if (nodeId == undefined)
|
||||
this.editStepDialog.open(async step => {
|
||||
await this.stepsEditorHelper.addRootNode({
|
||||
Id: uuid.v4(),
|
||||
Kind: kind,
|
||||
Step: step,
|
||||
StepId: step.id
|
||||
});
|
||||
});
|
||||
else
|
||||
this.editStepDialog.open(async step => {
|
||||
await this.stepsEditorHelper.addLeftNode(nodeId, {
|
||||
Id: uuid.v4(),
|
||||
Kind: kind,
|
||||
Step: step,
|
||||
StepId: step.id
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private addCatchNode(root: any, parent: any, kind: MaintenanceStepNodeKind) {
|
||||
this.editStepDialog.open(async step => {
|
||||
await this.stepsEditorHelper.addRightNode(root, parent, {
|
||||
Id: uuid.v4(),
|
||||
Kind: kind,
|
||||
Step: step,
|
||||
StepId: step.id
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
export class NodesGraphOptions {
|
||||
bodyElement: HTMLDivElement
|
||||
nodeGraphActionsElement: HTMLDivElement
|
||||
editStepModalElement: HTMLDivElement
|
||||
nodesInputElement: HTMLInputElement
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
export class StepValidationResult {
|
||||
isValid: boolean;
|
||||
payload: any;
|
||||
}
|
165
onecmonitor-server/Scripts/MaintenanceTask/stepsEditorHelper.ts
Normal file
165
onecmonitor-server/Scripts/MaintenanceTask/stepsEditorHelper.ts
Normal file
@ -0,0 +1,165 @@
|
||||
import Mermaid from "mermaid";
|
||||
|
||||
export class StepsEditorHelper {
|
||||
private schemaInput: HTMLInputElement;
|
||||
|
||||
constructor(input: HTMLInputElement) {
|
||||
this.schemaInput = input;
|
||||
}
|
||||
|
||||
public async init() {
|
||||
Mermaid.initialize({
|
||||
theme: 'dark',
|
||||
securityLevel: 'loose',
|
||||
startOnLoad: false,
|
||||
darkMode: true
|
||||
})
|
||||
|
||||
await this.redrawNodes()
|
||||
}
|
||||
|
||||
findNode(id: string, node: any): any | undefined {
|
||||
if (node.Id == id)
|
||||
return node;
|
||||
|
||||
const currentNode = node;
|
||||
|
||||
let foundNode = undefined;
|
||||
|
||||
if (currentNode.hasOwnProperty('RightNode') && currentNode.RightNode != undefined)
|
||||
foundNode = this.findNode(id, currentNode.RightNode);
|
||||
|
||||
if (foundNode == undefined && currentNode.hasOwnProperty('LeftNode') && currentNode.LeftNode != undefined)
|
||||
foundNode = this.findNode(id, currentNode.LeftNode);
|
||||
|
||||
return foundNode;
|
||||
}
|
||||
|
||||
deleteNode(node: any, removingNode: any) {
|
||||
if (node.hasOwnProperty('RightNode') && node.RightNode != undefined)
|
||||
{
|
||||
if (node.RightNode.Id == removingNode.Id)
|
||||
{
|
||||
delete node['RightNode'];
|
||||
delete node['RightNodeId'];
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
this.deleteNode(node.RightNode, removingNode);
|
||||
}
|
||||
|
||||
if (node.hasOwnProperty('LeftNode') && node.LeftNode != undefined)
|
||||
{
|
||||
if (node.LeftNode.Id == removingNode.Id)
|
||||
{
|
||||
delete node['LeftNode'];
|
||||
delete node['LeftNodeId'];
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
this.deleteNode(node.LeftNode, removingNode);
|
||||
}
|
||||
}
|
||||
|
||||
async addRootNode(node: any) {
|
||||
this.saveNode(node);
|
||||
await this.redrawNodes();
|
||||
}
|
||||
|
||||
async addLeftNode(parentId: string, node: any) {
|
||||
const root = this.getRootNode();
|
||||
const parent = this.findNode(parentId, root);
|
||||
parent.LeftNodeId = node.Id;
|
||||
parent.LeftNode = node;
|
||||
|
||||
this.saveNode(root);
|
||||
await this.redrawNodes();
|
||||
}
|
||||
|
||||
async addRightNode(root: any, parent: any, node: any) {
|
||||
parent.RightNodeId = node.Id;
|
||||
parent.RightNode = node;
|
||||
|
||||
this.saveNode(root);
|
||||
await this.redrawNodes();
|
||||
}
|
||||
|
||||
saveNode(node: any | undefined) {
|
||||
if (node == undefined)
|
||||
this.schemaInput.value = '';
|
||||
else
|
||||
this.schemaInput.value = JSON.stringify(node);
|
||||
}
|
||||
|
||||
getRootNode(): any | undefined {
|
||||
if (this.schemaInput.value == "")
|
||||
return undefined;
|
||||
else
|
||||
return JSON.parse(this.schemaInput.value);
|
||||
}
|
||||
|
||||
private nodeToGraphDefinition(currentNode: any): string {
|
||||
let currentNodeText = "";
|
||||
|
||||
let currentNodeLeft = currentNode.Id;
|
||||
|
||||
// is try/catch node
|
||||
if (currentNode.Kind == 1) {
|
||||
const node = currentNode;
|
||||
|
||||
currentNodeLeft = `${currentNodeLeft}{${currentNode.Step.title}}`
|
||||
|
||||
let needSetCurrentNodeText = true;
|
||||
|
||||
if (node.hasOwnProperty('LeftNode') && node.LeftNode != undefined) {
|
||||
currentNodeText += currentNodeLeft + " -->|Успешно| " + this.nodeToGraphDefinition(node.LeftNode) + '\r';
|
||||
needSetCurrentNodeText = false;
|
||||
}
|
||||
|
||||
if (node.hasOwnProperty('RightNode') && node.RightNode != undefined) {
|
||||
currentNodeText += currentNodeLeft + " -->|Ошибка| " + this.nodeToGraphDefinition(node.RightNode) + '\r';
|
||||
needSetCurrentNodeText = false;
|
||||
}
|
||||
|
||||
if (needSetCurrentNodeText)
|
||||
currentNodeText = currentNodeLeft;
|
||||
} else {
|
||||
const node = currentNode;
|
||||
|
||||
currentNodeText = `${currentNodeLeft}[${currentNode.Step.title}]`
|
||||
|
||||
if (node.hasOwnProperty('LeftNode') && node.LeftNode != undefined)
|
||||
currentNodeText += " --> " + this.nodeToGraphDefinition(node.LeftNode) + '\r';
|
||||
}
|
||||
|
||||
if (currentNodeText[currentNodeText.length - 1] != '\r')
|
||||
currentNodeText += '\r';
|
||||
|
||||
currentNodeText += `click ${currentNode.Id} OM.clickNode\r`;
|
||||
return currentNodeText;
|
||||
}
|
||||
|
||||
async redrawNodes() {
|
||||
const root = this.getRootNode();
|
||||
const nodesDefinition = root == undefined ? "" : this.nodeToGraphDefinition(root);
|
||||
|
||||
let graphDefinition = "flowchart TD\n" + nodesDefinition;
|
||||
await this.redrawGraph(graphDefinition);
|
||||
}
|
||||
|
||||
private async redrawGraph(definition: string) {
|
||||
document.querySelector('.mermaid')?.remove()
|
||||
|
||||
const graphBody = document.querySelector('#graphBody');
|
||||
|
||||
const mermaidContainer = document.createElement('div');
|
||||
mermaidContainer.classList.add('mermaid');
|
||||
mermaidContainer.classList.add('text-center');
|
||||
mermaidContainer.textContent = definition;
|
||||
graphBody.appendChild(mermaidContainer);
|
||||
|
||||
await Mermaid.run()
|
||||
}
|
||||
}
|
@ -6,14 +6,21 @@ import '../node_modules/bootstrap-icons/font/bootstrap-icons.css';
|
||||
|
||||
//modules
|
||||
import '../node_modules/bootstrap/dist/js/bootstrap.bundle.min'
|
||||
require('../Scripts/itemSelectionDialog')
|
||||
require('../Scripts/deleteDialog')
|
||||
require('../Scripts/techLog')
|
||||
require('../Scripts/infoBasesUpdatingTaskLog')
|
||||
require('./maintenanceTasks')
|
||||
require('./itemSelectionDialog')
|
||||
require('./deleteDialog')
|
||||
require('./techLog')
|
||||
require('./infoBasesUpdatingTaskLog')
|
||||
require('./infoBasesUpdateTask')
|
||||
|
||||
export * from '../Scripts/itemSelectionDialog'
|
||||
export * from '../Scripts/deleteDialog'
|
||||
export * from '../Scripts/techLog'
|
||||
export * from '../Scripts/infoBasesUpdatingTaskLog'
|
||||
export * from './maintenanceTasks'
|
||||
export * from './itemSelectionDialog'
|
||||
export * from './deleteDialog'
|
||||
export * from './techLog'
|
||||
export * from './infoBasesUpdatingTaskLog'
|
||||
export * from './infoBasesUpdateTask'
|
||||
export {EditStepDialog} from "./MaintenanceTask/editStepDialog";
|
||||
export {StepValidationResult} from "./MaintenanceTask/stepValidationResult";
|
||||
export {MaintenanceStepNodeKind} from "./MaintenanceTask/maintenanceStepNodeKind";
|
||||
export {NodesGraphOptions} from "./MaintenanceTask/nodesGraphOptions";
|
||||
export {NodesGraph} from "./MaintenanceTask/nodesGraph";
|
||||
export {NodesGraphActionsDialog} from "./MaintenanceTask/nodesGraphActionsDialog";
|
||||
export {StepsEditorHelper} from "./MaintenanceTask/stepsEditorHelper";
|
32
onecmonitor-server/Scripts/infoBasesUpdateTask.ts
Normal file
32
onecmonitor-server/Scripts/infoBasesUpdateTask.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import {NodesGraph} from "./MaintenanceTask/nodesGraph";
|
||||
|
||||
let nodesGraph: NodesGraph = undefined;
|
||||
|
||||
export async function initStepsEditor() {
|
||||
nodesGraph = new NodesGraph({
|
||||
nodesInputElement: getSchemaNodesElement(),
|
||||
editStepModalElement: getStepEditDialogElement(),
|
||||
nodeGraphActionsElement: getNodeActionsElement(),
|
||||
bodyElement: getGraphBodyElement()
|
||||
});
|
||||
}
|
||||
|
||||
export function clickNode(nodeId: string) {
|
||||
nodesGraph.handleNodeClick(nodeId);
|
||||
}
|
||||
|
||||
function getNodeActionsElement(): HTMLDivElement {
|
||||
return document.getElementById('node-actions') as HTMLDivElement;
|
||||
}
|
||||
|
||||
function getGraphBodyElement(): HTMLDivElement {
|
||||
return document.getElementById('graphBody') as HTMLDivElement;
|
||||
}
|
||||
|
||||
function getSchemaNodesElement(): HTMLInputElement {
|
||||
return document.getElementById('schema-nodes') as HTMLInputElement;
|
||||
}
|
||||
|
||||
function getStepEditDialogElement(): HTMLDivElement {
|
||||
return document.getElementById('edit-step-dialog') as HTMLDivElement;
|
||||
}
|
@ -15,6 +15,8 @@ using AutoMapper;
|
||||
using AutoMapper.QueryableExtensions;
|
||||
using OnecMonitor.Server.Helpers;
|
||||
using OneSTools.Common.Platform;
|
||||
using OneSTools.Common.Platform.RemoteAdministration;
|
||||
using OneSTools.Common.Platform.Services;
|
||||
|
||||
namespace OnecMonitor.Server.Services
|
||||
{
|
||||
@ -40,6 +42,7 @@ namespace OnecMonitor.Server.Services
|
||||
public event AgentDisconnectedHandler? AgentDisconnected;
|
||||
|
||||
public AgentConnection(Socket socket, TechLogProcessor techLogProcessor, IServiceProvider serviceProvider)
|
||||
: base(serviceProvider.GetRequiredService<ILogger<AgentConnection>>())
|
||||
{
|
||||
Socket = socket;
|
||||
|
||||
@ -315,7 +318,7 @@ namespace OnecMonitor.Server.Services
|
||||
{
|
||||
var task = await _appDbContext.UpdateInfoBaseTasks
|
||||
.Where(c => c.InfoBases.Any(i => i.Cluster.Agent.Id == AgentInstance!.Id))
|
||||
.Include(c => c.Configurations)
|
||||
.Include(c => c.Files)
|
||||
.Include(c => c.InfoBases)
|
||||
.ThenInclude(c => c.Credentials)
|
||||
.Include(c => c.InfoBases)
|
||||
|
@ -2,6 +2,7 @@ using System.ComponentModel;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
using OnecMonitor.Server.Models;
|
||||
using OneSTools.Common.Platform;
|
||||
using OneSTools.Common.Platform.Services;
|
||||
|
||||
namespace OnecMonitor.Server.ViewModels.Agents;
|
||||
|
||||
|
@ -1,6 +0,0 @@
|
||||
namespace OnecMonitor.Server.ViewModels.Configurations;
|
||||
|
||||
public class ConfigurationsIndexViewModel
|
||||
{
|
||||
public List<ConfigurationListItemViewModel> Items { get; init; } = [];
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using OnecMonitor.Server.Models;
|
||||
|
||||
namespace OnecMonitor.Server.ViewModels.Maintenance;
|
||||
|
||||
public class MaintenanceEditViewModel
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string Description { get; set; } = string.Empty;
|
||||
|
||||
public List<SelectableItemViewModel> InfoBases { get; set; } = [];
|
||||
[ValidateNever]
|
||||
public List<SelectableItemViewModel> AvailableInfoBases { get; set; } = [];
|
||||
|
||||
public List<MaintenanceStepViewModel> MaintenanceSteps { get; set; } = [];
|
||||
|
||||
[ValidateNever]
|
||||
public SelectList MaintenanceStepKinds { get; set; } = null!;
|
||||
|
||||
[ValidateNever]
|
||||
public SelectList ExternalDataProcessors { get; set; } = null!;
|
||||
|
||||
[ValidateNever]
|
||||
public SelectList Extensions { get; set; } = null!;
|
||||
|
||||
[ValidateNever]
|
||||
public SelectList ConfigUpdates { get; set; } = null!;
|
||||
|
||||
[ValidateNever]
|
||||
public SelectList Configs { get; set; } = null!;
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
namespace OnecMonitor.Server.ViewModels.Maintenance;
|
||||
|
||||
public class MaintenanceIndexViewModel
|
||||
{
|
||||
public List<MaintenanceListItemViewModel> Items { get; set; } = [];
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
namespace OnecMonitor.Server.ViewModels.Maintenance;
|
||||
|
||||
public class MaintenanceListItemViewModel
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string Description { get; set; } = string.Empty;
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
using OnecMonitor.Server.Models.MaintenanceTasks;
|
||||
|
||||
namespace OnecMonitor.Server.ViewModels.MaintenanceTasks;
|
||||
|
||||
public class MaintenanceStepNodeViewModel
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public MaintenanceStepNodeKind Kind { get; set; }
|
||||
|
||||
[ValidateNever]
|
||||
public Guid? LeftNodeId { get; set; }
|
||||
[ValidateNever]
|
||||
public Guid? RightNodeId { get; set; }
|
||||
|
||||
public Guid StepId { get; set; }
|
||||
|
||||
public MaintenanceStepNodeViewModel? LeftNode { get; set; } = null!;
|
||||
public MaintenanceStepNodeViewModel? RightNode { get; set; } = null!;
|
||||
|
||||
public MaintenanceStepViewModel Step { get; set; } = null!;
|
||||
}
|
@ -1,21 +1,35 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using OnecMonitor.Server.Extensions;
|
||||
using OnecMonitor.Server.Helpers;
|
||||
using OnecMonitor.Server.Models;
|
||||
using OnecMonitor.Server.Models.MaintenanceTasks;
|
||||
|
||||
namespace OnecMonitor.Server.ViewModels.Maintenance;
|
||||
namespace OnecMonitor.Server.ViewModels.MaintenanceTasks;
|
||||
|
||||
public class MaintenanceStepViewModel
|
||||
{
|
||||
[JsonPropertyName("id")]
|
||||
public Guid Id { get; set; }
|
||||
[JsonPropertyName("kind")]
|
||||
public MaintenanceStepKind Kind { get; set; }
|
||||
[ValidateNever]
|
||||
public SelectList Kinds { get; set; } = null!;
|
||||
|
||||
[ValidateNever]
|
||||
[JsonPropertyName("accessCode")]
|
||||
public string AccessCode { get; set; } = string.Empty;
|
||||
[ValidateNever]
|
||||
[JsonPropertyName("message")]
|
||||
public string Message { get; set; } = string.Empty;
|
||||
|
||||
[ValidateNever]
|
||||
[JsonPropertyName("fileId")]
|
||||
public Guid? FileId { get; set; }
|
||||
[ValidateNever]
|
||||
public SelectList Files { get; set; } = null!;
|
||||
|
||||
[ValidateNever]
|
||||
public string AccessCode { get; set; } = string.Empty;
|
||||
[ValidateNever]
|
||||
public string Message { get; set; } = string.Empty;
|
||||
[JsonPropertyName("title")]
|
||||
public string Title => Kind.GetDisplay();
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
using System.ComponentModel;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Newtonsoft.Json;
|
||||
using OnecMonitor.Server.Models;
|
||||
|
||||
namespace OnecMonitor.Server.ViewModels.MaintenanceTasks;
|
||||
|
||||
public class MaintenanceTaskEditViewModel
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
[DisplayName("Описание")]
|
||||
public string Description { get; set; } = string.Empty;
|
||||
|
||||
[DisplayName("Информационные базы")]
|
||||
public List<SelectableItemViewModel> InfoBases { get; set; } = [];
|
||||
[ValidateNever]
|
||||
public List<SelectableItemViewModel> AvailableInfoBases { get; set; } = [];
|
||||
|
||||
public string SerializedStepNode { get; set; } = string.Empty;
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
namespace OnecMonitor.Server.ViewModels.MaintenanceTasks;
|
||||
|
||||
public class MaintenanceTaskListItemViewModel
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string Description { get; set; } = string.Empty;
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
namespace OnecMonitor.Server.ViewModels.MaintenanceTasks;
|
||||
|
||||
public class MaintenanceTasksIndexViewModel
|
||||
{
|
||||
public List<MaintenanceTaskListItemViewModel> Items { get; set; } = [];
|
||||
}
|
@ -12,9 +12,9 @@ public class UpdateInfoBaseTaskEditViewModel
|
||||
|
||||
[ValidateNever]
|
||||
[DisplayName("Конфигурации")]
|
||||
public List<SelectableItemViewModel> Configurations { get; set; } = [];
|
||||
public List<SelectableItemViewModel> Files { get; set; } = [];
|
||||
[ValidateNever]
|
||||
public List<SelectableItemViewModel> AvailableConfigurations { get; set; } = [];
|
||||
public List<SelectableItemViewModel> AvailableFiles { get; set; } = [];
|
||||
|
||||
[DisplayName("Информационные базы")]
|
||||
public List<SelectableItemViewModel> InfoBases { get; set; } = [];
|
||||
|
@ -1,8 +1,8 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace OnecMonitor.Server.ViewModels.Configurations;
|
||||
namespace OnecMonitor.Server.ViewModels.V8Files;
|
||||
|
||||
public class ConfigurationEditViewModel
|
||||
public class V8FileEditViewModel
|
||||
{
|
||||
public Guid Id { get; init; }
|
||||
public string Name { get; set; } = string.Empty;
|
@ -1,6 +1,6 @@
|
||||
namespace OnecMonitor.Server.ViewModels.Configurations;
|
||||
namespace OnecMonitor.Server.ViewModels.V8Files;
|
||||
|
||||
public class ConfigurationListItemViewModel
|
||||
public class V8FileListItemViewModel
|
||||
{
|
||||
public Guid Id { get; init; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
@ -17,8 +17,10 @@ public class ConfigurationListItemViewModel
|
||||
return "Обновление";
|
||||
else if (IsConfiguration)
|
||||
return "Конфигурация";
|
||||
else
|
||||
else if (IsExtension)
|
||||
return "Расширение";
|
||||
else
|
||||
return "Внешняя обработка";
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
namespace OnecMonitor.Server.ViewModels.V8Files;
|
||||
|
||||
public class V8FilesIndexViewModel
|
||||
{
|
||||
public List<V8FileListItemViewModel> Items { get; init; } = [];
|
||||
}
|
101
onecmonitor-server/Views/MaintenanceTasks/Edit.cshtml
Normal file
101
onecmonitor-server/Views/MaintenanceTasks/Edit.cshtml
Normal file
@ -0,0 +1,101 @@
|
||||
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@using NuGet.Protocol
|
||||
@using OnecMonitor.Server.Extensions
|
||||
@using OnecMonitor.Server.Helpers
|
||||
@using OnecMonitor.Server.Models.MaintenanceTasks
|
||||
@using OnecMonitor.Server.ViewModels
|
||||
@model OnecMonitor.Server.ViewModels.MaintenanceTasks.MaintenanceTaskEditViewModel
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Задача обслуживания";
|
||||
}
|
||||
|
||||
<h2 class="mb-3">Задача обслуживания</h2>
|
||||
|
||||
<form method="post" asp-action="Save" style="height: 100%">
|
||||
<div class="mb-3">
|
||||
<button class="btn btn-primary" type="submit">Сохранить</button>
|
||||
<button class="btn btn-secondary" onclick="history.back()" type="button">Назад</button>
|
||||
</div>
|
||||
|
||||
<input type="text" hidden class="form-control" asp-for="Id" value="@Model.Id">
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label" asp-for="Description">Описание</label>
|
||||
<input type="text" class="form-control" asp-for="Description" value="@Model.Description">
|
||||
<span asp-validation-for="Description" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
@await Html.PartialAsync("SelectItemDialog", new SelectItemDialogViewModel(
|
||||
Html.DisplayNameFor(c => c.InfoBases),
|
||||
nameof(Model.InfoBases),
|
||||
Model.InfoBases,
|
||||
Model.AvailableInfoBases))
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<div class="card">
|
||||
<div class="card-header">Схема</div>
|
||||
<input id="schema-nodes" type="text" hidden class="form-control" name="SerializedStepNode" value="@Model.SerializedStepNode">
|
||||
<div id="graphBody" class="card-body" style="min-height: 200px"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
<div class="modal" id="select-step-kind-dialog" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Выберите тип шага</h5>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="list-group">
|
||||
@foreach (var item in Enum.GetValues<MaintenanceStepKind>())
|
||||
{
|
||||
<button type="button" data-kind="@((int)item)" class="list-group-item list-group-item-action">@item.GetDisplay()</button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal" id="edit-step-dialog" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Изменение шага обслуживания</h5>
|
||||
</div>
|
||||
<div class="modal-body"></div>
|
||||
<div class="modal-footer">
|
||||
<button id="save-btn" type="button" class="btn btn-primary">Сохранить</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="node-actions" popover class="border-0 bg-transparent position-fixed">
|
||||
<div class="card">
|
||||
<div class="card-header">Выберите действие</div>
|
||||
<div class="card-body">
|
||||
<div class="list-group">
|
||||
<button id="delete-step-btn" type="button" class="list-group-item list-group-item-action">Удалить</button>
|
||||
<button id="edit-step-btn" type="button" class="list-group-item list-group-item-action">Изменить</button>
|
||||
<button id="add-step-btn" type="button" class="list-group-item list-group-item-action">Добавить шаг</button>
|
||||
<button id="add-error-step-btn" type="button" class="list-group-item list-group-item-action">Добавить шаг исключения</button>
|
||||
<button id="add-binary-step-btn" type="button" class="list-group-item list-group-item-action">Добавить шаг c обработкой исключения</button>
|
||||
<button id="add-error-binary-step-btn" type="button" class="list-group-item list-group-item-action">Добавить шаг исключения c обработкой исключения</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@section Scripts
|
||||
{
|
||||
<script type="text/javascript">
|
||||
OM.initStepsEditor();
|
||||
</script>
|
||||
}
|
48
onecmonitor-server/Views/MaintenanceTasks/EditStep.cshtml
Normal file
48
onecmonitor-server/Views/MaintenanceTasks/EditStep.cshtml
Normal file
@ -0,0 +1,48 @@
|
||||
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@using OnecMonitor.Server.Extensions
|
||||
@using OnecMonitor.Server.Models.MaintenanceTasks
|
||||
@using OnecMonitor.Server.ViewModels
|
||||
@model OnecMonitor.Server.ViewModels.MaintenanceTasks.MaintenanceStepViewModel
|
||||
|
||||
<form method="post" style="height: 100%">
|
||||
|
||||
<input type="text" hidden class="form-control" asp-for="Id" value="@Model.Id">
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label" asp-for="Kind">Тип шага</label>
|
||||
<select asp-for="Kind" class="form-control" asp-items="Model.Kinds"></select>
|
||||
<span asp-validation-for="Kind" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
@if (Model.Kind == MaintenanceStepKind.LockConnections)
|
||||
{
|
||||
<div class="mb-3">
|
||||
<label class="form-label" asp-for="AccessCode">Код доступа</label>
|
||||
<input type="text" class="form-control" asp-for="AccessCode" value="@Model.AccessCode">
|
||||
<span asp-validation-for="AccessCode" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label" asp-for="Message">Сообщение</label>
|
||||
<input type="text" class="form-control" asp-for="Message" value="@Model.Message">
|
||||
<span asp-validation-for="Message" class="text-danger"></span>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (Model is
|
||||
{
|
||||
Kind: MaintenanceStepKind.LoadConfiguration or
|
||||
MaintenanceStepKind.UpdateConfiguration or
|
||||
MaintenanceStepKind.LoadExtension or
|
||||
MaintenanceStepKind.StartExternalDataProcessor
|
||||
})
|
||||
{
|
||||
<div class="mb-3">
|
||||
<label class="form-label" asp-for="FileId">Файл</label>
|
||||
<select asp-for="FileId" class="form-control" asp-items="Model.Files"></select>
|
||||
<span asp-validation-for="FileId" class="text-danger"></span>
|
||||
</div>
|
||||
}
|
||||
|
||||
</form>
|
||||
|
43
onecmonitor-server/Views/MaintenanceTasks/Index.cshtml
Normal file
43
onecmonitor-server/Views/MaintenanceTasks/Index.cshtml
Normal file
@ -0,0 +1,43 @@
|
||||
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@using OnecMonitor.Server.ViewModels
|
||||
@using OnecMonitor.Server.ViewModels.Agents;
|
||||
@model OnecMonitor.Server.ViewModels.MaintenanceTasks.MaintenanceTasksIndexViewModel
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Задачи обслуживания";
|
||||
}
|
||||
|
||||
<h2 class="mb-3">Задачи обслуживания</h2>
|
||||
|
||||
<div class="mb-3">
|
||||
<a class="btn btn-primary" asp-action="Edit" role="button">Добавить</a>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="col">Описание</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var item in Model.Items)
|
||||
{
|
||||
<tr>
|
||||
<td>@item.Description</td>
|
||||
<td class="text-center">
|
||||
<a class="btn btn-outline-warning btn-sm" asp-action="Edit" asp-route-id="@item.Id">Изменить</a>
|
||||
<a class="btn btn-outline-danger btn-sm" onclick="OM.openDeleteDialog('@item.Id', '@item.Description')">Удалить</a>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@await Html.PartialAsync("DeleteDialog", new DeleteDialogViewModel
|
||||
{
|
||||
Controller = "MaintenanceTasks",
|
||||
Action = "Delete"
|
||||
})
|
@ -51,8 +51,13 @@
|
||||
</a>
|
||||
</li>
|
||||
<li class="w-100">
|
||||
<a asp-controller="Configurations" asp-action="Index" class="nav-link text-white px-0">
|
||||
<small class="d-none d-sm-inline">Конфигурации</small>
|
||||
<a asp-controller="V8Files" asp-action="Index" class="nav-link text-white px-0">
|
||||
<small class="d-none d-sm-inline">Файлы 1С</small>
|
||||
</a>
|
||||
</li>
|
||||
<li class="w-100">
|
||||
<a asp-controller="MaintenanceTasks" asp-action="Index" class="nav-link text-white px-0">
|
||||
<small class="d-none d-sm-inline">Задачи обслуживания</small>
|
||||
</a>
|
||||
</li>
|
||||
<li class="w-100">
|
||||
|
@ -1,4 +1,7 @@
|
||||
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@using OnecMonitor.Server.Extensions
|
||||
@using OnecMonitor.Server.Helpers
|
||||
@using OnecMonitor.Server.Models.MaintenanceTasks
|
||||
@using OnecMonitor.Server.ViewModels
|
||||
@model OnecMonitor.Server.ViewModels.UpdateInfoBaseTasks.UpdateInfoBaseTaskEditViewModel
|
||||
|
||||
@ -24,10 +27,10 @@
|
||||
|
||||
<div class="mb-3">
|
||||
@await Html.PartialAsync("SelectItemDialog", new SelectItemDialogViewModel(
|
||||
Html.DisplayNameFor(c => c.Configurations),
|
||||
nameof(Model.Configurations),
|
||||
Model.Configurations,
|
||||
Model.AvailableConfigurations))
|
||||
Html.DisplayNameFor(c => c.Files),
|
||||
nameof(Model.Files),
|
||||
Model.Files,
|
||||
Model.AvailableFiles))
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
|
@ -1,38 +1,38 @@
|
||||
@using OnecMonitor.Server.ViewModels.Configurations;
|
||||
@model ConfigurationEditViewModel
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Конфигурация";
|
||||
}
|
||||
|
||||
<h2 class="mb-3">Конфигурация</h2>
|
||||
|
||||
<form method="post" asp-action="Save" enctype="multipart/form-data" style="height: 100%">
|
||||
<div class="mb-3">
|
||||
<button class="btn btn-primary" type="submit">Сохранить</button>
|
||||
<button class="btn btn-secondary" onclick="history.back()" type="button">Назад</button>
|
||||
</div>
|
||||
|
||||
<input type="text" hidden class="form-control" asp-for="Id" value="@Model.Id">
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label" asp-for="Name">Имя</label>
|
||||
<input type="text" class="form-control" asp-for="Name" value="@Model.Name">
|
||||
<span asp-validation-for="Name" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label" asp-for="Version">Версия</label>
|
||||
<input type="text" class="form-control" asp-for="Version" value="@Model.Version">
|
||||
<span asp-validation-for="Version" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
@if (Model.Id == Guid.Empty)
|
||||
{
|
||||
<div>
|
||||
<input class="form-control" accept=".cf,.cfu,.cfe" type="file" asp-for="File">
|
||||
<span asp-validation-for="File" class="text-danger" disabled></span>
|
||||
</div>
|
||||
}
|
||||
|
||||
@using OnecMonitor.Server.ViewModels.V8Files;
|
||||
@model V8FileEditViewModel
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Файл 1С";
|
||||
}
|
||||
|
||||
<h2 class="mb-3">Файл 1С</h2>
|
||||
|
||||
<form method="post" asp-action="Save" enctype="multipart/form-data" style="height: 100%">
|
||||
<div class="mb-3">
|
||||
<button class="btn btn-primary" type="submit">Сохранить</button>
|
||||
<button class="btn btn-secondary" onclick="history.back()" type="button">Назад</button>
|
||||
</div>
|
||||
|
||||
<input type="text" hidden class="form-control" asp-for="Id" value="@Model.Id">
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label" asp-for="Name">Имя</label>
|
||||
<input type="text" class="form-control" asp-for="Name" value="@Model.Name">
|
||||
<span asp-validation-for="Name" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label" asp-for="Version">Версия</label>
|
||||
<input type="text" class="form-control" asp-for="Version" value="@Model.Version">
|
||||
<span asp-validation-for="Version" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
@if (Model.Id == Guid.Empty)
|
||||
{
|
||||
<div>
|
||||
<input class="form-control" accept=".cf,.cfu,.cfe,.epf" type="file" asp-for="File">
|
||||
<span asp-validation-for="File" class="text-danger" disabled></span>
|
||||
</div>
|
||||
}
|
||||
|
||||
</form>
|
@ -1,45 +1,45 @@
|
||||
@using OnecMonitor.Server.ViewModels
|
||||
@model OnecMonitor.Server.ViewModels.Configurations.ConfigurationsIndexViewModel
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Конфигурации";
|
||||
}
|
||||
|
||||
<h2 class="mb-3">Конфигурации</h2>
|
||||
|
||||
<div class="mb-3">
|
||||
<a class="btn btn-primary" asp-action="Edit" role="button">Добавить</a>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Имя</th>
|
||||
<th>Версия</th>
|
||||
<th>Тип</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var item in Model.Items)
|
||||
{
|
||||
<tr>
|
||||
<td>@item.Name</td>
|
||||
<td>@item.Version</td>
|
||||
<td>@item.Type</td>
|
||||
<td class="text-center">
|
||||
<a class="btn btn-outline-warning btn-sm" asp-action="Edit" asp-route-id="@item.Id">Изменить</a>
|
||||
<a class="btn btn-outline-danger btn-sm" onclick="OM.openDeleteDialog('@item.Id', '@item.Name')">Удалить</a>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@await Html.PartialAsync("DeleteDialog", new DeleteDialogViewModel
|
||||
{
|
||||
Controller = "Configurations",
|
||||
Action = "Delete"
|
||||
})
|
||||
@using OnecMonitor.Server.ViewModels
|
||||
@model OnecMonitor.Server.ViewModels.V8Files.V8FilesIndexViewModel
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Файлы 1С";
|
||||
}
|
||||
|
||||
<h2 class="mb-3">Файлы 1С</h2>
|
||||
|
||||
<div class="mb-3">
|
||||
<a class="btn btn-primary" asp-action="Edit" role="button">Добавить</a>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Имя</th>
|
||||
<th>Версия</th>
|
||||
<th>Тип</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var item in Model.Items)
|
||||
{
|
||||
<tr>
|
||||
<td>@item.Name</td>
|
||||
<td>@item.Version</td>
|
||||
<td>@item.Type</td>
|
||||
<td class="text-center">
|
||||
<a class="btn btn-outline-warning btn-sm" asp-action="Edit" asp-route-id="@item.Id">Изменить</a>
|
||||
<a class="btn btn-outline-danger btn-sm" onclick="OM.openDeleteDialog('@item.Id', '@item.Name')">Удалить</a>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@await Html.PartialAsync("DeleteDialog", new DeleteDialogViewModel
|
||||
{
|
||||
Controller = "V8Files",
|
||||
Action = "Delete"
|
||||
})
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Default": "Trace",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
|
@ -50,6 +50,7 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="9.0.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
@ -78,6 +79,14 @@
|
||||
<_ContentIncludedByDefault Remove="Views\MaintenanceTasks\Steps\LoadExtension.cshtml" />
|
||||
<_ContentIncludedByDefault Remove="Views\MaintenanceTasks\Steps\LockConnections.cshtml" />
|
||||
<_ContentIncludedByDefault Remove="Views\MaintenanceTasks\Steps\UpdateConfiguration.cshtml" />
|
||||
<_ContentIncludedByDefault Remove="Views\UpdateInfoBaseTasks\Steps\LockConnections.cshtml" />
|
||||
<_ContentIncludedByDefault Remove="Views\UpdateInfoBaseTasks\Steps\UpdateConfiguration.cshtml" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Models\MaintenanceTasks\LoadConfigurationStep.cs" />
|
||||
<Compile Remove="Migrations\20250225154747_Initial.cs" />
|
||||
<Compile Remove="Migrations\20250225154747_Initial.Designer.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ProjectExtensions>
|
||||
|
@ -13,7 +13,9 @@
|
||||
"chart.js": "4.4.1",
|
||||
"signalr": "^2.4.3",
|
||||
"vis-timeline": "7.7.3",
|
||||
"visjs-network": "4.25.0"
|
||||
"visjs-network": "4.25.0",
|
||||
"mermaid": "^11.4.1",
|
||||
"uuid": "^11.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/ace": "^0.0.52",
|
||||
|
@ -7,6 +7,11 @@
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"moduleResolution": "node",
|
||||
"sourceRoot": "Scripts",
|
||||
"mapRoot": "wwwroot",
|
||||
"typeRoots": [
|
||||
"node_modules/@types"
|
||||
]
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
|
@ -1,41 +0,0 @@
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
|
||||
|
||||
module.exports = {
|
||||
entry: {
|
||||
script: [
|
||||
'./Scripts/index.ts'
|
||||
]
|
||||
},
|
||||
devtool: 'inline-source-map',
|
||||
optimization: {
|
||||
minimize: false,
|
||||
usedExports: false
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
loader: 'ts-loader',
|
||||
options: {
|
||||
transpileOnly: true
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.css$/i,
|
||||
use: ['style-loader', 'css-loader']
|
||||
},
|
||||
],
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.tsx', '.ts', '.js'],
|
||||
},
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'wwwroot'),
|
||||
filename: 'dist/app.js',
|
||||
library: {
|
||||
|
||||
}
|
||||
},
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user