diff --git a/Kit.Core.Helpers/Class1.cs b/Kit.Core.Helpers/Class1.cs
deleted file mode 100644
index 3f2f7f6..0000000
--- a/Kit.Core.Helpers/Class1.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace Kit.Core.Helpers
-{
- public class Class1
- {
-
- }
-}
diff --git a/Kit.Core.Helpers/Kit.Core.Helpers.sln b/Kit.Core.Helpers/Kit.Core.Helpers.sln
deleted file mode 100644
index 96ab0af..0000000
--- a/Kit.Core.Helpers/Kit.Core.Helpers.sln
+++ /dev/null
@@ -1,25 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 17
-VisualStudioVersion = 17.14.36301.6 d17.14
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kit.Core.Helpers", "Kit.Core.Helpers.csproj", "{54CF91D7-8299-4DB9-ABF6-4F141A52523A}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {54CF91D7-8299-4DB9-ABF6-4F141A52523A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {54CF91D7-8299-4DB9-ABF6-4F141A52523A}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {54CF91D7-8299-4DB9-ABF6-4F141A52523A}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {54CF91D7-8299-4DB9-ABF6-4F141A52523A}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {9D8ECF0E-0537-42C4-8DDC-5934B57A874F}
- EndGlobalSection
-EndGlobal
diff --git a/Kit.Core.Helpers/obj/Debug/net8.0/Kit.Core.Helpers.AssemblyInfoInputs.cache b/Kit.Core.Helpers/obj/Debug/net8.0/Kit.Core.Helpers.AssemblyInfoInputs.cache
deleted file mode 100644
index ef09e43..0000000
--- a/Kit.Core.Helpers/obj/Debug/net8.0/Kit.Core.Helpers.AssemblyInfoInputs.cache
+++ /dev/null
@@ -1 +0,0 @@
-1de42344d47904be68c52d310f760c9a33b8396e0e8e253be53c0fe180725da2
diff --git a/Kit.Core.Helpers/obj/project.nuget.cache b/Kit.Core.Helpers/obj/project.nuget.cache
deleted file mode 100644
index b727142..0000000
--- a/Kit.Core.Helpers/obj/project.nuget.cache
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "version": 2,
- "dgSpecHash": "oGlU1raL4Zc=",
- "success": true,
- "projectFilePath": "C:\\KIT\\Kit.Core\\Kit.Core.Helpers\\Kit.Core.Helpers.csproj",
- "expectedPackageFiles": [],
- "logs": []
-}
\ No newline at end of file
diff --git a/Kit.Core.sln b/Kit.Core.sln
index eacdac6..bb4865e 100644
--- a/Kit.Core.sln
+++ b/Kit.Core.sln
@@ -3,7 +3,23 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.14.36301.6
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kit.Core.Helpers", "Kit.Core.Helpers\Kit.Core.Helpers.csproj", "{AF9E1670-5E30-EEEC-3261-A64E6069FE41}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "LibCommon", "LibCommon", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kit.Core.Helpers", "LibCommon\Kit.Core.Helpers\Kit.Core.Helpers.csproj", "{45C84BE0-7434-60C9-C314-2ABBEC2244EC}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Minio", "LibExternal\Minio\Minio.csproj", "{2EB985EC-7D25-FA14-84F6-8158236A99CF}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "LibExternal", "LibExternal", "{46D7C78C-2A15-4F28-8498-58FE53B592F0}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Npgsql", "LibExternal\Npgsql\Npgsql.csproj", "{AF14796C-37B9-E123-FD4C-4C212045BE8F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.IO.Hashing", "LibExternal\System.IO.Hashing\System.IO.Hashing.csproj", "{B3FA8F79-9DF2-476D-AA0B-F02A201D5997}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Reactive", "LibExternal\System.Reactive\System.Reactive.csproj", "{837A418C-8CCE-5A21-E61F-EA456EEBECBE}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kit.Helpers.OpenXml", "LibCommon\Kit.Helpers.OpenXml\Kit.Helpers.OpenXml.csproj", "{2067E7B0-78BB-95B5-68E2-B38C1D158621}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kit.Sign", "LibCommon\Kit.Sign\Kit.Sign.csproj", "{4F7F1524-5EC0-455A-3046-36E2252BC850}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -11,14 +27,47 @@ Global
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {AF9E1670-5E30-EEEC-3261-A64E6069FE41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {AF9E1670-5E30-EEEC-3261-A64E6069FE41}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {AF9E1670-5E30-EEEC-3261-A64E6069FE41}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {AF9E1670-5E30-EEEC-3261-A64E6069FE41}.Release|Any CPU.Build.0 = Release|Any CPU
+ {45C84BE0-7434-60C9-C314-2ABBEC2244EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {45C84BE0-7434-60C9-C314-2ABBEC2244EC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {45C84BE0-7434-60C9-C314-2ABBEC2244EC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {45C84BE0-7434-60C9-C314-2ABBEC2244EC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2EB985EC-7D25-FA14-84F6-8158236A99CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2EB985EC-7D25-FA14-84F6-8158236A99CF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2EB985EC-7D25-FA14-84F6-8158236A99CF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2EB985EC-7D25-FA14-84F6-8158236A99CF}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AF14796C-37B9-E123-FD4C-4C212045BE8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AF14796C-37B9-E123-FD4C-4C212045BE8F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AF14796C-37B9-E123-FD4C-4C212045BE8F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AF14796C-37B9-E123-FD4C-4C212045BE8F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B3FA8F79-9DF2-476D-AA0B-F02A201D5997}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B3FA8F79-9DF2-476D-AA0B-F02A201D5997}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B3FA8F79-9DF2-476D-AA0B-F02A201D5997}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B3FA8F79-9DF2-476D-AA0B-F02A201D5997}.Release|Any CPU.Build.0 = Release|Any CPU
+ {837A418C-8CCE-5A21-E61F-EA456EEBECBE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {837A418C-8CCE-5A21-E61F-EA456EEBECBE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {837A418C-8CCE-5A21-E61F-EA456EEBECBE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {837A418C-8CCE-5A21-E61F-EA456EEBECBE}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2067E7B0-78BB-95B5-68E2-B38C1D158621}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2067E7B0-78BB-95B5-68E2-B38C1D158621}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2067E7B0-78BB-95B5-68E2-B38C1D158621}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2067E7B0-78BB-95B5-68E2-B38C1D158621}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4F7F1524-5EC0-455A-3046-36E2252BC850}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4F7F1524-5EC0-455A-3046-36E2252BC850}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4F7F1524-5EC0-455A-3046-36E2252BC850}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4F7F1524-5EC0-455A-3046-36E2252BC850}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {45C84BE0-7434-60C9-C314-2ABBEC2244EC} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
+ {2EB985EC-7D25-FA14-84F6-8158236A99CF} = {46D7C78C-2A15-4F28-8498-58FE53B592F0}
+ {AF14796C-37B9-E123-FD4C-4C212045BE8F} = {46D7C78C-2A15-4F28-8498-58FE53B592F0}
+ {B3FA8F79-9DF2-476D-AA0B-F02A201D5997} = {46D7C78C-2A15-4F28-8498-58FE53B592F0}
+ {837A418C-8CCE-5A21-E61F-EA456EEBECBE} = {46D7C78C-2A15-4F28-8498-58FE53B592F0}
+ {2067E7B0-78BB-95B5-68E2-B38C1D158621} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
+ {4F7F1524-5EC0-455A-3046-36E2252BC850} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
+ EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {9D8ECF0E-0537-42C4-8DDC-5934B57A874F}
EndGlobalSection
diff --git a/LibCommon/Kit.Core.Helpers/AssemblyHelper.cs b/LibCommon/Kit.Core.Helpers/AssemblyHelper.cs
new file mode 100644
index 0000000..1458f9b
--- /dev/null
+++ b/LibCommon/Kit.Core.Helpers/AssemblyHelper.cs
@@ -0,0 +1,55 @@
+using System.Reflection;
+
+namespace Kit.Helpers
+{
+ public class AssemblyHelper
+ {
+ public static void CheckSignatures()
+ {
+ // Получаем эталонный открытый ключ из текущей сборки
+ Assembly currentAssembly = Assembly.GetExecutingAssembly();
+ var referencePublicKey = currentAssembly.GetName().GetPublicKey();
+
+ // Проверяем, что текущая сборка подписана
+ if (referencePublicKey == null || referencePublicKey.Length == 0)
+ {
+ throw new ApplicationException("The current build is not signed.");
+ }
+
+ // Получаем список всех загруженных сборок
+ var assemblies = AppDomain.CurrentDomain.GetAssemblies().OrderBy(x => x.FullName).ToList();
+
+ bool anyInvalid = false;
+
+ // Проверяем каждую сборку
+ foreach (var assembly in assemblies)
+ {
+ AssemblyName assemblyName = assembly.GetName();
+ var publicKey = assemblyName.GetPublicKey();
+
+ if (assemblyName.Name.ToLowerStartsWith("RiskProf") == false)
+ {
+ continue;
+ }
+
+ // Если сборка не подписана
+ if (publicKey == null || publicKey.Length == 0)
+ {
+ anyInvalid = true;
+ Console.WriteLine($"The assembly {assembly.FullName} is not signed.");
+ }
+ // Сравниваем открытый ключ с эталонным
+ else if (referencePublicKey.SequenceEqual(publicKey) == false)
+ {
+ anyInvalid = true;
+ Console.WriteLine($"The assembly {assembly.FullName} is signed with a different key.");
+ }
+ }
+
+ if (anyInvalid)
+ {
+ throw new ApplicationException("Not all signatures match the signature of the current build.");
+ }
+ }
+ }
+}
diff --git a/LibCommon/Kit.Core.Helpers/Attributes/NeedToGenerateJsConstAttribute.cs b/LibCommon/Kit.Core.Helpers/Attributes/NeedToGenerateJsConstAttribute.cs
new file mode 100644
index 0000000..bf34a68
--- /dev/null
+++ b/LibCommon/Kit.Core.Helpers/Attributes/NeedToGenerateJsConstAttribute.cs
@@ -0,0 +1,11 @@
+namespace Kit.Helpers
+{
+ /// Атрибут, указывающий на то, что для данного статического класса / enum нужно сформировать js-объект
+ /// Описания класса и полей, которые нужно включить в генерацию, нужно указывать с помощью
+ [AttributeUsage(AttributeTargets.Class|AttributeTargets.Enum, Inherited = false, AllowMultiple = false)]
+ public sealed class NeedToGenerateJsConstAttribute : Attribute
+ {
+ /// Создавать ли константный объект "{имя класса/enum}Titles" с теми же ключами, но значениями из
+ public bool NeedTitlesObject { get; set; }
+ }
+}
diff --git a/LibCommon/Kit.Core.Helpers/Attributes/NeedToGenerateJsDocAttribute.cs b/LibCommon/Kit.Core.Helpers/Attributes/NeedToGenerateJsDocAttribute.cs
new file mode 100644
index 0000000..c60e7b9
--- /dev/null
+++ b/LibCommon/Kit.Core.Helpers/Attributes/NeedToGenerateJsDocAttribute.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace Kit.Helpers
+{
+ /// Атрибут, указывающий на то, что для данного класса нужно сформировать JsDoc-
+ /// Описания класса и полей, которые нужно включить в генерацию, нужно указывать с помощью
+ [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
+ public sealed class NeedToGenerateJsDocAttribute : Attribute { }
+}
diff --git a/LibCommon/Kit.Core.Helpers/Auth/ICurrentUser.cs b/LibCommon/Kit.Core.Helpers/Auth/ICurrentUser.cs
new file mode 100644
index 0000000..e02ba8b
--- /dev/null
+++ b/LibCommon/Kit.Core.Helpers/Auth/ICurrentUser.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Kit.Helpers.Auth
+{
+ public interface ICurrentUser
+ {
+ string GetUserToken();
+ }
+}
diff --git a/LibCommon/Kit.Core.Helpers/Auth/SecurityKeyAttribute.cs b/LibCommon/Kit.Core.Helpers/Auth/SecurityKeyAttribute.cs
new file mode 100644
index 0000000..a2e36ec
--- /dev/null
+++ b/LibCommon/Kit.Core.Helpers/Auth/SecurityKeyAttribute.cs
@@ -0,0 +1,54 @@
+namespace Kit.Helpers.Auth
+{
+ using Kit.Helpers;
+ using Microsoft.AspNetCore.Mvc;
+ using Microsoft.AspNetCore.Mvc.Filters;
+ using Microsoft.AspNetCore.Routing;
+ using Microsoft.Extensions.Configuration;
+
+ public class SecurityKeyFilter : IAuthorizationFilter
+ {
+ private string _authServiceUrl;
+ private ISecurityKeyService _securityKeyService;
+
+ public SecurityKeyFilter(IConfiguration configuration, ISecurityKeyService securityKeyService)
+ {
+ _authServiceUrl = configuration["Auth:ServiceUrl"];
+ _securityKeyService = securityKeyService;
+ }
+
+
+ public bool AllowMultiple { get { return false; } }
+
+
+ public void OnAuthorization(AuthorizationFilterContext context)
+ {
+
+ var routeData = context.HttpContext.GetRouteData();
+
+ //// securityKeyName
+ string securityKeyName = routeData.DataTokens["APP_SecurityKeyName"] as string;
+ if (string.IsNullOrWhiteSpace(securityKeyName)) return;
+
+ //// requestSecurityKeyValue
+ string requestSecurityKeyValue = context.HttpContext.Request.Headers.ReadAuthHeader();
+ if (!_securityKeyService.CheckKey(securityKeyName, requestSecurityKeyValue))
+ {
+
+ string method = context.HttpContext.Request.Method.ToLower();
+ string href = $"{_authServiceUrl}/noaccess/{method}";
+ string hrefWithQuery = href + context.HttpContext.Request.QueryString;
+ context.Result = new RedirectResult(hrefWithQuery);
+
+ if (method.Equals("get"))
+ {
+ context.Result = new RedirectResult(hrefWithQuery);
+ }
+ if (method.Equals("post"))
+ {
+ context.Result = new JsonResult(new { State = 307, Url = hrefWithQuery });
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/LibCommon/Kit.Core.Helpers/Auth/SecurityKeyService.cs b/LibCommon/Kit.Core.Helpers/Auth/SecurityKeyService.cs
new file mode 100644
index 0000000..0cc80fb
--- /dev/null
+++ b/LibCommon/Kit.Core.Helpers/Auth/SecurityKeyService.cs
@@ -0,0 +1,50 @@
+namespace Kit.Helpers.Auth
+{
+ using System.Collections.Generic;
+
+ public interface ISecurityKeyRegisterService
+ {
+ ISecurityKeyRegisterService RegisterKey(string name, string value);
+ }
+
+ public interface ISecurityKeyService
+ {
+ bool CheckKey(string name, string value);
+ string GetKey(string name);
+ }
+
+ public class SecurityKeyService : ISecurityKeyService, ISecurityKeyRegisterService
+ {
+ private readonly IDictionary _securityKeys;
+
+ public SecurityKeyService()
+ {
+ _securityKeys = new Dictionary();
+ }
+
+ public bool CheckKey(string name, string value)
+ {
+ string targetValue;
+ if(_securityKeys.TryGetValue(name, out targetValue))
+ {
+ if (string.IsNullOrWhiteSpace(targetValue)) return true;
+ return targetValue == value;
+ }
+ return false;
+ }
+
+ public string GetKey(string name)
+ {
+ string targetValue;
+ return _securityKeys.TryGetValue(name, out targetValue)
+ ? targetValue
+ : string.Empty;
+ }
+
+ public ISecurityKeyRegisterService RegisterKey(string name, string value)
+ {
+ _securityKeys[name] = value;
+ return this;
+ }
+ }
+}
\ No newline at end of file
diff --git a/LibCommon/Kit.Core.Helpers/Cache/BaseCacheProvider.cs b/LibCommon/Kit.Core.Helpers/Cache/BaseCacheProvider.cs
new file mode 100644
index 0000000..3f40d0e
--- /dev/null
+++ b/LibCommon/Kit.Core.Helpers/Cache/BaseCacheProvider.cs
@@ -0,0 +1,96 @@
+using Microsoft.Extensions.Caching.Memory;
+using Kit.Helpers;
+
+namespace Kit.Helpers.Cache
+{
+ public abstract class BaseCacheProvider : ICacheProvider
+ {
+
+ private readonly Dictionary _lockObjects = new Dictionary(1000);
+
+ private readonly object _keysLock = new object();
+
+ public abstract bool Contains(string cacheName);
+ public abstract void PurgeCacheItems(string cacheName, bool isPrefix = false);
+ public abstract void SetCacheData(string cacheName, TObject data, MemoryCacheEntryOptions? setProperties = null, PostEvictionDelegate? callback = null);
+ public abstract TResult GetCacheData(string cacheName);
+
+ protected string CacheNameNormalize(string casheName)
+ {
+ if (casheName == null) return string.Empty;
+
+ return casheName.ToLower();
+ }
+ protected object GetLockObject(string cacheName)
+ {
+ lock (_keysLock)
+ {
+ if (!this._lockObjects.ContainsKey(cacheName)) this._lockObjects.Add(cacheName, new object());
+ return this._lockObjects[cacheName];
+ }
+ }
+
+ public virtual string CreateCacheName(object prefix, params object[] args)
+ {
+ prefix = prefix != null ? prefix.ToString()!.AddSufixIsNotEmpty("_") : string.Empty;
+
+ string end = string.Empty;
+ if (!args.IsNullOrEmpty())
+ {
+ end = args.Where(x => x != null).Select(x => x.ToString()).Join("_");
+ }
+
+ return $"{prefix}{end}";
+ }
+
+ public virtual TResult GetCacheData(string cacheName, Func getMethod, MemoryCacheEntryOptions setProperties = null)
+ {
+ cacheName = CacheNameNormalize(cacheName);
+
+ Check.ArgumentIsNotNull(getMethod, "getMethod");
+ if (cacheName.IsNullOrEmpty()) return (TResult)getMethod();
+
+ // если есть данные в кеше но возвращаем
+ TResult data = GetCacheData(cacheName);
+ if (data != null) return data;
+
+ lock (this.GetLockObject(cacheName))
+ {
+ // повторная проверка после блокировки
+ data = this.GetCacheData(cacheName);
+ if (data != null) return data;
+
+ // если в кеше данных нет то выполняем метод
+ data = getMethod();
+ this.SetCacheData(cacheName, data, setProperties);
+ }
+
+ return data;
+ }
+
+ public TResult GetCacheData(string cacheName, Func getMethod, MemoryCacheEntryOptions? setProperties = null, PostEvictionDelegate? callback = null)
+ {
+ cacheName = CacheNameNormalize(cacheName);
+
+ Check.ArgumentIsNotNull(getMethod, "getMethod");
+ if (cacheName.IsNullOrEmpty()) return (TResult)getMethod();
+
+ // если есть данные в кеше но возвращаем
+ TResult data = GetCacheData(cacheName);
+ if (data != null) return data;
+
+ lock (this.GetLockObject(cacheName))
+ {
+ // повторная проверка после блокировки
+ data = this.GetCacheData(cacheName);
+ if (data != null) return data;
+
+ // если в кеше данных нет то выполняем метод
+ data = getMethod();
+ this.SetCacheData(cacheName, data, setProperties, callback);
+ }
+
+ return data;
+ }
+ }
+}
diff --git a/LibCommon/Kit.Core.Helpers/Cache/ICacheProvider.cs b/LibCommon/Kit.Core.Helpers/Cache/ICacheProvider.cs
new file mode 100644
index 0000000..117ebca
--- /dev/null
+++ b/LibCommon/Kit.Core.Helpers/Cache/ICacheProvider.cs
@@ -0,0 +1,20 @@
+namespace Kit.Helpers.Cache
+{
+ using Microsoft.Extensions.Caching.Memory;
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using System.Threading.Tasks;
+
+ public interface ICacheProvider
+ {
+ string CreateCacheName(object prefix, params object[] args);
+ bool Contains(string cacheName);
+ void PurgeCacheItems(string cacheName, bool isPrefix = false);
+
+ void SetCacheData(string cacheName, TObject data, MemoryCacheEntryOptions? setProperties = null, PostEvictionDelegate? callback = null);
+ TResult GetCacheData(string cacheName);
+ TResult GetCacheData(string cacheName, Func getMethod, MemoryCacheEntryOptions? setProperties = null, PostEvictionDelegate? callback = null);
+ }
+}
diff --git a/LibCommon/Kit.Core.Helpers/Cache/MemoryCacheExtensions.cs b/LibCommon/Kit.Core.Helpers/Cache/MemoryCacheExtensions.cs
new file mode 100644
index 0000000..5ef71d9
--- /dev/null
+++ b/LibCommon/Kit.Core.Helpers/Cache/MemoryCacheExtensions.cs
@@ -0,0 +1,23 @@
+namespace Kit.Helpers.Cache
+{
+ using Microsoft.Extensions.Caching.Memory;
+ using System;
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Reflection;
+
+ public static class MemoryCacheExtensions
+ {
+ private static readonly Func GetEntriesCollection = Delegate.CreateDelegate(
+ typeof(Func),
+ typeof(MemoryCache).GetProperty("EntriesCollection", BindingFlags.NonPublic | BindingFlags.Instance).GetGetMethod(true),
+ throwOnBindFailure: true) as Func;
+
+ public static IEnumerable GetKeys(this IMemoryCache memoryCache) =>
+ ((IDictionary)GetEntriesCollection((MemoryCache)memoryCache)).Keys;
+
+ public static IEnumerable GetKeys(this IMemoryCache memoryCache) =>
+ GetKeys(memoryCache).OfType();
+ }
+}
diff --git a/LibCommon/Kit.Core.Helpers/Cache/MemoryCacheProvider.cs b/LibCommon/Kit.Core.Helpers/Cache/MemoryCacheProvider.cs
new file mode 100644
index 0000000..4d8b74c
--- /dev/null
+++ b/LibCommon/Kit.Core.Helpers/Cache/MemoryCacheProvider.cs
@@ -0,0 +1,84 @@
+namespace Kit.Helpers.Cache
+{
+ using Microsoft.Extensions.Caching.Memory;
+ using Kit.Helpers;
+ using System;
+ using System.Collections;
+ using System.Collections.Generic;
+
+ public interface IHttpCacheProvider : ICacheProvider { }
+
+ public class MemoryCacheProvider : BaseCacheProvider, IHttpCacheProvider
+ {
+ private IMemoryCache _memoryCache;
+
+ public MemoryCacheProvider(IMemoryCache memoryCache)
+ {
+ _memoryCache = memoryCache;
+ }
+
+
+ public override bool Contains(string cacheName)
+ {
+ cacheName = this.CacheNameNormalize(cacheName);
+ object obj = null;
+ return _memoryCache.TryGetValue(cacheName, out obj);
+ }
+
+ public override TResult GetCacheData(string cacheName)
+ {
+ cacheName = CacheNameNormalize(cacheName);
+
+ object? result = this._memoryCache.Get(cacheName);
+ if (result == null) return default!;
+
+ Check.IsTrue(result is TResult, $"Cache value has type:{result.GetType().FullName}. ArgumentType:{typeof(TResult).FullName}");
+
+ return (TResult)result;
+ }
+
+
+ public override void PurgeCacheItems(string cacheName, bool isPrefix = false)
+ {
+ cacheName = CacheNameNormalize(cacheName);
+
+ if (isPrefix)
+ {
+ var itemsToRemove = new List();
+
+ foreach (DictionaryEntry item in this._memoryCache.GetKeys())
+ {
+ string key = item.Key.ToString()!;
+ if (key.StartsWith(cacheName))
+ {
+ itemsToRemove.Add(key);
+ }
+ }
+ //удаляем элементы кэша
+ itemsToRemove.ForEach(x => this._memoryCache.Remove(x));
+ }
+ else
+ {
+ this._memoryCache.Remove(cacheName);
+ }
+ }
+
+ public override void SetCacheData(string cacheName, TObject data, MemoryCacheEntryOptions? setProperties = null, PostEvictionDelegate? callback = null)
+ {
+ if (data == null) return;
+ cacheName = CacheNameNormalize(cacheName);
+
+ if (setProperties == null)
+ {
+ setProperties = new MemoryCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromDays(1) };
+ setProperties.Priority = CacheItemPriority.Normal;
+ if (callback != null)
+ {
+ setProperties.RegisterPostEvictionCallback(callback);
+ }
+ }
+
+ this._memoryCache.Set(cacheName, data, setProperties);
+ }
+ }
+}
\ No newline at end of file
diff --git a/LibCommon/Kit.Core.Helpers/Check.cs b/LibCommon/Kit.Core.Helpers/Check.cs
new file mode 100644
index 0000000..94bcbb2
--- /dev/null
+++ b/LibCommon/Kit.Core.Helpers/Check.cs
@@ -0,0 +1,257 @@
+namespace Kit.Helpers
+{
+ using System;
+ using System.Diagnostics;
+ using System.Diagnostics.CodeAnalysis;
+
+ ///
+ /// a static class that checks if parameter is invalid and raises an exception in this case
+ ///
+ public static class Check
+ {
+ [DebuggerStepThrough]
+ public static void ArgumentIsNotNull([AllowNull][NotNull] object argument, string name)
+ {
+ if (argument == null)
+ {
+ throw new ArgumentNullException(name);
+ }
+ }
+
+ [DebuggerStepThrough]
+ public static void ArgumentIsNotNull([AllowNull][NotNull] object argument, string name, string message)
+ {
+ if (argument == null)
+ {
+ throw new ArgumentException(message, name);
+ }
+ }
+
+ [DebuggerStepThrough]
+ public static void ArgumentIsNotNullOrEmpty([AllowNull][NotNull] string argument, string name)
+ {
+ if (string.IsNullOrWhiteSpace(argument))
+ {
+ throw new ArgumentNullException(name);
+ }
+ }
+
+ [DebuggerStepThrough]
+ public static void ArgumentIsNotNullOrEmpty([AllowNull][NotNull] string argument, string name, string message)
+ {
+ if (string.IsNullOrWhiteSpace(argument))
+ {
+ throw new ArgumentException(message, name);
+ }
+ }
+
+ [DebuggerStepThrough]
+ public static void IntegerArgumentPositive(Int32 value, string name)
+ {
+ if (value < 1) throw new ArgumentOutOfRangeException(name, value, string.Empty);
+ }
+
+ [DebuggerStepThrough]
+ public static void IntegerArgumentPositive(Int32? value, string name)
+ {
+ if (!value.HasValue) throw new ArgumentNullException(name);
+ if (value.Value < 1) throw new ArgumentOutOfRangeException(name, value.Value, string.Empty);
+ }
+
+ [DebuggerStepThrough]
+ public static void IntegerPositive(Int16 value, string message)
+ {
+ if (value < 1) throw new InvalidOperationException(message);
+ }
+
+ [DebuggerStepThrough]
+ public static void IntegerPositive(Int32 value, string message)
+ {
+ if (value < 1) throw new InvalidOperationException(message);
+ }
+
+ [DebuggerStepThrough]
+ public static void IntegerPositive(Int64 value, string message)
+ {
+ if (value < 1) throw new InvalidOperationException(message);
+ }
+
+ [DebuggerStepThrough]
+ public static void IntegerPositive(Int32? value, string message)
+ {
+ if (!value.HasValue) throw new InvalidOperationException(message);
+ if (value.Value < 1) throw new InvalidOperationException(message);
+ }
+
+ [DebuggerStepThrough]
+ public static void IntegerArgumentPositive(Int64 value, string name)
+ {
+ if (value < 1) throw new ArgumentOutOfRangeException(name, value, string.Empty);
+ }
+
+ [DebuggerStepThrough]
+ public static void IntegerArgumentPositive(Int64? value, string name)
+ {
+ if (!value.HasValue) throw new ArgumentNullException(name);
+ if (value.Value < 1) throw new ArgumentOutOfRangeException(name, value.Value, string.Empty);
+ }
+
+ [DebuggerStepThrough]
+ public static void StringArgumentIsNotNullOrEmpty(string str, string? paramName, string? message)
+ {
+ if (string.IsNullOrEmpty(str))
+ throw new ArgumentNullException(paramName: paramName, message: message);
+
+ str = str.Trim();
+ if (string.IsNullOrEmpty(str))
+ throw new ArgumentNullException(paramName: paramName, message: message);
+ }
+
+ [DebuggerStepThrough]
+ public static void IsModelStateValid(Microsoft.AspNetCore.Mvc.ModelBinding.ModelStateDictionary modelState, string? message = null)
+ {
+ if (modelState.IsValid) return;
+ if (string.IsNullOrWhiteSpace(message) == false)
+ {
+ throw new InvalidOperationException(message);
+ }
+
+ var allErrors = modelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage).ToList();
+
+ throw new InvalidOperationException(allErrors.Join(Environment.NewLine));
+ }
+
+ [DebuggerStepThrough]
+ public static void IsTrue(bool argument, string message)
+ {
+ if (argument == false)
+ {
+ throw new InvalidOperationException(message);
+ }
+ }
+
+ [DebuggerStepThrough]
+ public static void IsNotTrue(bool argument, string message)
+ {
+ if (argument == true)
+ {
+ throw new InvalidOperationException(message);
+ }
+ }
+
+ [DebuggerStepThrough]
+ public static void IsNull([AllowNull][NotNull] object argument, string message)
+ {
+ if (argument != null)
+ {
+ throw new InvalidOperationException(message);
+ }
+ }
+
+ [DebuggerStepThrough]
+ public static void IsNotNull([AllowNull][NotNull] object argument, string message)
+ {
+ if (argument == null)
+ {
+ throw new InvalidOperationException(message);
+ }
+ }
+
+ [DebuggerStepThrough]
+ public static void IsNullOrEmpty(Guid? argument, string message)
+ {
+ if (argument.HasValue == false || argument.Value.Equals(Guid.Empty))
+ {
+ return;
+ }
+
+ throw new InvalidOperationException(message);
+ }
+
+ [DebuggerStepThrough]
+ public static void IsEmpty(Guid argument, string message)
+ {
+ if (argument.Equals(Guid.Empty))
+ {
+ return;
+ }
+
+ throw new InvalidOperationException(message);
+ }
+
+ [DebuggerStepThrough]
+ public static void IsNotNullOrEmpty([AllowNull][NotNull]Guid? argument, string message)
+ {
+ if (argument.HasValue && argument.Value.Equals(Guid.Empty) == false)
+ {
+ return;
+ }
+
+ throw new InvalidOperationException(message);
+ }
+
+ [DebuggerStepThrough]
+ public static void IsNotEmpty(Guid argument, string message)
+ {
+ if (argument.Equals(Guid.Empty) == false)
+ {
+ return;
+ }
+
+ throw new InvalidOperationException(message);
+ }
+
+ [DebuggerStepThrough]
+ public static void IsNullOrEmpty([AllowNull][NotNull] IEnumerable argument, string message)
+ {
+ if (argument.IsNullOrEmpty() == false)
+ {
+ throw new InvalidOperationException(message);
+ }
+ }
+
+ [DebuggerStepThrough]
+ public static void IsNotNullOrEmpty([AllowNull][NotNull] IEnumerable argument, string message)
+ {
+ if (argument.IsNullOrEmpty())
+ {
+ throw new InvalidOperationException(message);
+ }
+ }
+ ///
+ /// Checks whether argument is of specified type
+ /// Throws InvalidOperationException if argument is of wrong type
+ ///
+ /// the argument to be checked
+ /// the Type
+ /// Implied to contain a {0} placeholder within in order to be replaced with string representation of the type param
+ [DebuggerStepThrough]
+ public static void ArgumentIsOfType(object? argument, Type type, string message)
+ {
+ Check.ArgumentIsNotNull(argument, "argument");
+ Check.ArgumentIsNotNull(type, "type");
+
+ if (type != argument.GetType())
+ {
+ throw new InvalidOperationException(string.Format(message, type.ToString()));
+ }
+ }
+
+ [DebuggerStepThrough]
+ public static void IsNotNullOrWhiteSpace([AllowNull][NotNull] string? value, string message)
+ {
+ if (string.IsNullOrWhiteSpace(value))
+ {
+ throw new InvalidOperationException(message);
+ }
+ }
+ [DebuggerStepThrough]
+ public static void IsNullOrWhiteSpace([AllowNull][NotNull] string? value, string message)
+ {
+ if (string.IsNullOrWhiteSpace(value) == false)
+ {
+ throw new InvalidOperationException(message);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/LibCommon/Kit.Core.Helpers/Config/DependencyInjectionXmlUpdateItems.cs b/LibCommon/Kit.Core.Helpers/Config/DependencyInjectionXmlUpdateItems.cs
new file mode 100644
index 0000000..5ecb6ba
--- /dev/null
+++ b/LibCommon/Kit.Core.Helpers/Config/DependencyInjectionXmlUpdateItems.cs
@@ -0,0 +1,36 @@
+using Microsoft.Extensions.DependencyInjection;
+using Kit.Helpers.Log;
+using Kit.Helpers.Repository.Xml;
+using Kit.Helpers.Service;
+
+namespace Kit.Helpers.Config;
+
+public static class DependencyInjectionXmlUpdateItems
+{
+ public static IServiceCollection AddModuleXmlVersionService(this IServiceCollection services)
+ {
+ services.AddSingleton();
+ services.AddSingleton();
+ services.RegisterUpdateItemsViaReflection();
+
+ return services;
+ }
+
+ private static void RegisterUpdateItemsViaReflection(this IServiceCollection services)
+ {
+ var interfaceType = typeof(IXmlVersionUpdateItem);
+
+ var callStackLog = new CallStackLog();
+ callStackLog.BeginCall("Get Assemblies");
+ var assemblies = AppDomain.CurrentDomain.GetAssemblies();
+ callStackLog.EndCall();
+
+ callStackLog.BeginCall("Get All Implementations");
+ IEnumerable implementationTypes = assemblies.SelectMany(x => x.GetTypes()).Where(t => interfaceType.IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract);
+ callStackLog.EndCall();
+
+ var message = callStackLog.LogMessage;
+
+ implementationTypes.ForEach(implementationType => services.AddTransient(interfaceType, implementationType));
+ }
+}
\ No newline at end of file
diff --git a/LibCommon/Kit.Core.Helpers/Config/StaticFileConfigBuilder.cs b/LibCommon/Kit.Core.Helpers/Config/StaticFileConfigBuilder.cs
new file mode 100644
index 0000000..403cb9f
--- /dev/null
+++ b/LibCommon/Kit.Core.Helpers/Config/StaticFileConfigBuilder.cs
@@ -0,0 +1,81 @@
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.StaticFiles;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.FileProviders;
+
+namespace Kit.Helpers.Config;
+
+public class StaticFileConfigBuilder
+ where TIApplicationBuilder : IApplicationBuilder
+{
+ private readonly List _contentModules;
+
+ private Action _onPrepareResponse;
+
+ internal StaticFileConfigBuilder()
+ {
+ _contentModules = new List();
+ _onPrepareResponse = ctx =>
+ {
+ ctx.Context.Response.Headers.Append("Cache-Control", "public,max-age=3600");
+ };
+ }
+
+ public StaticFileConfigBuilder WithContentModule(string moduleName)
+ {
+ _contentModules.Add(moduleName);
+ return this;
+ }
+
+ public StaticFileConfigBuilder WithOnPrepareResponse(Action ctx)
+ {
+ _onPrepareResponse = ctx;
+ return this;
+ }
+
+ internal TIApplicationBuilder Apply(TIApplicationBuilder app)
+ {
+ IWebHostEnvironment env = app.ApplicationServices.GetRequiredService();
+ // основной wwwroot
+ List fileProviderItems = new List { env.WebRootFileProvider };
+
+ if (_contentModules.Count > 0)
+ {
+#if DEBUG
+ fileProviderItems.Add(new PhysicalFileProvider(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "wwwroot")));
+#else
+ fileProviders.AddRange(_contentModules.Select(moduleName => new PhysicalFileProvider(Path.Combine(_env.ContentRootPath, "wwwroot", "_content", moduleName))));
+#endif
+ }
+
+ var fileProvider = new CompositeFileProvider(fileProviderItems);
+ env.WebRootFileProvider = fileProvider;
+ app.UseStaticFiles(new StaticFileOptions
+ {
+ FileProvider = fileProvider,
+ RequestPath = "",
+ OnPrepareResponse = _onPrepareResponse
+ });
+
+ return app;
+ }
+}
+
+public static class StaticFileConfigBuilderExt
+{
+
+ public static TIApplicationBuilder UseCustomStaticFiles(this TIApplicationBuilder app, Action> options)
+ where TIApplicationBuilder : IApplicationBuilder
+ {
+ var config = new StaticFileConfigBuilder();
+
+ if (options != null)
+ options(config);
+
+ config.Apply(app);
+
+ return app;
+ }
+}
\ No newline at end of file
diff --git a/LibCommon/Kit.Core.Helpers/ConfigurationHelper.cs b/LibCommon/Kit.Core.Helpers/ConfigurationHelper.cs
new file mode 100644
index 0000000..88e1b10
--- /dev/null
+++ b/LibCommon/Kit.Core.Helpers/ConfigurationHelper.cs
@@ -0,0 +1,95 @@
+using Microsoft.Extensions.Configuration;
+
+namespace Kit.Helpers
+{
+ public interface IConfigurationItem
+ {
+ DateTime FillDate { get; set; }
+
+ void Init(IConfiguration configuration);
+ }
+
+ public class ConfigurationHelper
+ {
+ private readonly string _prefix;
+ private readonly IConfiguration _configuration;
+
+ public ConfigurationHelper(IConfiguration configuration) : this(configuration, string.Empty)
+ {
+ _configuration = configuration;
+ }
+
+ public ConfigurationHelper(IConfiguration configuration, string prefix)
+ {
+ _configuration = configuration;
+ _prefix = prefix;
+ }
+
+ private string CreateKey(string itemKey)
+ {
+ return string.IsNullOrEmpty(_prefix)
+ ? itemKey.ToString()
+ : $"{_prefix}:{itemKey}";
+ }
+
+ public string GetConnectionString(string name, bool required = true)
+ {
+ string connectionStringName = this.CreateKey(name);
+
+ string connectionString = _configuration.GetConnectionString(connectionStringName);
+
+ if (required)
+ {
+ Check.StringArgumentIsNotNullOrEmpty(connectionString, null, $"connection string not set in config. name={connectionStringName}");
+ }
+
+ return connectionString;
+ }
+
+ public string GetAppSettingValue(object settingsItemKey, bool required = true, string? defaultValue = null)
+ {
+ // create key
+ string appSettingsKey = this.CreateKey(settingsItemKey?.ToString() ?? string.Empty);
+
+ // read
+ string? configurationValue = _configuration[appSettingsKey] ?? defaultValue;
+
+ // validate
+ if (required && string.IsNullOrWhiteSpace(configurationValue))
+ {
+ throw new Exception($"{appSettingsKey} not set in appSettings section");
+ }
+
+ return configurationValue ?? string.Empty;
+ }
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// The .
+ public IConfigurationSection GetSection(string name, bool required = true)
+ {
+ // create key
+ string appSettingsKey = this.CreateKey(name.ToString());
+
+ if (required)
+ {
+ return _configuration.GetRequiredSection(appSettingsKey);
+ }
+ else
+ {
+ return _configuration.GetSection(appSettingsKey);
+ }
+ }
+
+ public TEntity? Get(string name)
+ {
+ // create key
+ string key = this.CreateKey(name.ToString());
+
+ return _configuration.GetSection(key).Get();
+ }
+ }
+}
\ No newline at end of file
diff --git a/LibCommon/Kit.Core.Helpers/ControllerExtensions.cs b/LibCommon/Kit.Core.Helpers/ControllerExtensions.cs
new file mode 100644
index 0000000..5bd4169
--- /dev/null
+++ b/LibCommon/Kit.Core.Helpers/ControllerExtensions.cs
@@ -0,0 +1,106 @@
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.ModelBinding;
+using Microsoft.AspNetCore.Mvc.ModelBinding.Binders;
+using System.Reflection;
+
+public static class ControllerExtensions
+{
+ public static async Task InvokeViewComponentWithModelBindingAsync(
+ this Controller controller,
+ Type viewComponentType)
+ {
+
+ var viewComponent = GetViewComponentInstance(controller, viewComponentType);
+ var invokeMethod = GetInvokeMethod(viewComponentType);
+ var parameters = invokeMethod.GetParameters();
+
+ var parameterValues = await BindParametersAsync(controller, parameters);
+
+ return await ExecuteViewComponentAsync(viewComponent, invokeMethod, parameterValues);
+ }
+
+ private static ViewComponent GetViewComponentInstance(Controller controller, Type viewComponentType)
+ {
+ var instance = controller.HttpContext.RequestServices.GetService(viewComponentType) as ViewComponent;
+ if (instance == null)
+ throw new InvalidOperationException($"ViewComponent {viewComponentType.Name} not found.");
+ return instance;
+ }
+
+ private static MethodInfo GetInvokeMethod(Type viewComponentType)
+ {
+ var method = viewComponentType.GetMethod("InvokeAsync") ?? viewComponentType.GetMethod("Invoke");
+ if (method == null)
+ throw new InvalidOperationException("ViewComponent does not have Invoke/InvokeAsync method.");
+ return method;
+ }
+
+ private static async Task