Merge branch 'feature/backend-refactor' into feature/asc-federatedlogin-refactor
This commit is contained in:
commit
aa102f9435
2
.gitignore
vendored
2
.gitignore
vendored
@ -10,7 +10,7 @@
|
||||
/build/deploy
|
||||
*.log
|
||||
/products/ASC.People/Data/
|
||||
Data/
|
||||
/Data
|
||||
Logs/
|
||||
**/.DS_Store
|
||||
.eslintcache
|
||||
|
@ -76,11 +76,11 @@ public abstract class BaseStartup
|
||||
|
||||
if (kafkaConfiguration != null)
|
||||
{
|
||||
DIHelper.TryAdd(typeof(ICacheNotify<>), typeof(KafkaCache<>));
|
||||
DIHelper.TryAdd(typeof(ICacheNotify<>), typeof(KafkaCacheNotify<>));
|
||||
}
|
||||
else if (redisConfiguration != null)
|
||||
{
|
||||
DIHelper.TryAdd(typeof(ICacheNotify<>), typeof(RedisCache<>));
|
||||
DIHelper.TryAdd(typeof(ICacheNotify<>), typeof(RedisCacheNotify<>));
|
||||
|
||||
services.AddStackExchangeRedisExtensions<NewtonsoftSerializer>(redisConfiguration);
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ global using System.Threading;
|
||||
global using System.Threading.Tasks;
|
||||
global using System.Web;
|
||||
global using System.Xml.Linq;
|
||||
global using StackExchange.Redis.Extensions.Newtonsoft;
|
||||
|
||||
global using ASC.Api.Core;
|
||||
global using ASC.Api.Core.Auth;
|
||||
global using ASC.Api.Core.Convention;
|
||||
@ -81,3 +81,4 @@ global using NLog;
|
||||
global using NLog.Extensions.Logging;
|
||||
|
||||
global using StackExchange.Redis.Extensions.Core.Configuration;
|
||||
global using StackExchange.Redis.Extensions.Newtonsoft;
|
@ -24,9 +24,6 @@
|
||||
<EmbeddedResource Include="Utils\TimeZoneConverter\windowsZones.xml" />
|
||||
<EmbeddedResource Include="Utils\TimeZoneConverter\timeZoneNames.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Remove="Notify\AWSEmail.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ARSoft.Tools.NetStandard.DXSdata" Version="1.0.0" />
|
||||
<PackageReference Include="Autofac.Configuration" Version="6.0.0" />
|
||||
@ -64,7 +61,4 @@
|
||||
<Protobuf Include="protos\distributed_task_cache.proto" />
|
||||
<Protobuf Include="protos\distributed_task_cancelation.proto" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Notify\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -23,24 +23,21 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Caching
|
||||
{
|
||||
namespace ASC.Common.Caching;
|
||||
|
||||
[Singletone]
|
||||
public class AscCacheNotify
|
||||
{
|
||||
private ICacheNotify<AscCacheItem> CacheNotify { get; }
|
||||
private readonly ICacheNotify<AscCacheItem> _cacheNotify;
|
||||
|
||||
public AscCacheNotify(ICacheNotify<AscCacheItem> cacheNotify)
|
||||
{
|
||||
CacheNotify = cacheNotify;
|
||||
_cacheNotify = cacheNotify;
|
||||
|
||||
CacheNotify.Subscribe((item) => { OnClearCache(); }, CacheNotifyAction.Any);
|
||||
_cacheNotify.Subscribe((item) => { OnClearCache(); }, CacheNotifyAction.Any);
|
||||
}
|
||||
|
||||
public void ClearCache()
|
||||
{
|
||||
CacheNotify.Publish(new AscCacheItem { Id = Guid.NewGuid().ToString() }, CacheNotifyAction.Any);
|
||||
}
|
||||
public void ClearCache() => _cacheNotify.Publish(new AscCacheItem { Id = Guid.NewGuid().ToString() }, CacheNotifyAction.Any);
|
||||
|
||||
public static void OnClearCache()
|
||||
{
|
||||
@ -56,18 +53,18 @@ namespace ASC.Common.Caching
|
||||
[Singletone]
|
||||
public class AscCache : ICache
|
||||
{
|
||||
private IMemoryCache MemoryCache { get; }
|
||||
private ConcurrentDictionary<string, object> MemoryCacheKeys { get; }
|
||||
private readonly IMemoryCache _memoryCache;
|
||||
private readonly ConcurrentDictionary<string, object> _memoryCacheKeys;
|
||||
|
||||
public AscCache(IMemoryCache memoryCache)
|
||||
{
|
||||
MemoryCache = memoryCache;
|
||||
MemoryCacheKeys = new ConcurrentDictionary<string, object>();
|
||||
_memoryCache = memoryCache;
|
||||
_memoryCacheKeys = new ConcurrentDictionary<string, object>();
|
||||
}
|
||||
|
||||
public T Get<T>(string key) where T : class
|
||||
{
|
||||
return MemoryCache.Get<T>(key);
|
||||
return _memoryCache.Get<T>(key);
|
||||
}
|
||||
|
||||
public void Insert(string key, object value, TimeSpan sligingExpiration)
|
||||
@ -76,8 +73,8 @@ namespace ASC.Common.Caching
|
||||
.SetSlidingExpiration(sligingExpiration)
|
||||
.RegisterPostEvictionCallback(EvictionCallback);
|
||||
|
||||
MemoryCache.Set(key, value, options);
|
||||
MemoryCacheKeys.TryAdd(key, null);
|
||||
_memoryCache.Set(key, value, options);
|
||||
_memoryCacheKeys.TryAdd(key, null);
|
||||
}
|
||||
|
||||
public void Insert(string key, object value, DateTime absolutExpiration)
|
||||
@ -86,43 +83,37 @@ namespace ASC.Common.Caching
|
||||
.SetAbsoluteExpiration(absolutExpiration == DateTime.MaxValue ? DateTimeOffset.MaxValue : new DateTimeOffset(absolutExpiration))
|
||||
.RegisterPostEvictionCallback(EvictionCallback);
|
||||
|
||||
MemoryCache.Set(key, value, options);
|
||||
MemoryCacheKeys.TryAdd(key, null);
|
||||
}
|
||||
|
||||
private void EvictionCallback(object key, object value, EvictionReason reason, object state)
|
||||
{
|
||||
MemoryCacheKeys.TryRemove(key.ToString(), out _);
|
||||
_memoryCache.Set(key, value, options);
|
||||
_memoryCacheKeys.TryAdd(key, null);
|
||||
}
|
||||
|
||||
public void Remove(string key)
|
||||
{
|
||||
MemoryCache.Remove(key);
|
||||
_memoryCache.Remove(key);
|
||||
}
|
||||
|
||||
public void Remove(Regex pattern)
|
||||
{
|
||||
var copy = MemoryCacheKeys.ToDictionary(p => p.Key, p => p.Value);
|
||||
var copy = _memoryCacheKeys.ToDictionary(p => p.Key, p => p.Value);
|
||||
var keys = copy.Select(p => p.Key).Where(k => pattern.IsMatch(k));
|
||||
|
||||
foreach (var key in keys)
|
||||
{
|
||||
MemoryCache.Remove(key);
|
||||
_memoryCache.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public ConcurrentDictionary<string, T> HashGetAll<T>(string key)
|
||||
{
|
||||
return MemoryCache.GetOrCreate(key, r => new ConcurrentDictionary<string, T>());
|
||||
}
|
||||
public ConcurrentDictionary<string, T> HashGetAll<T>(string key) =>
|
||||
_memoryCache.GetOrCreate(key, r => new ConcurrentDictionary<string, T>());
|
||||
|
||||
public T HashGet<T>(string key, string field)
|
||||
{
|
||||
if (MemoryCache.TryGetValue<ConcurrentDictionary<string, T>>(key, out var dic) && dic.TryGetValue(field, out var value))
|
||||
if (_memoryCache.TryGetValue<ConcurrentDictionary<string, T>>(key, out var dic)
|
||||
&& dic.TryGetValue(field, out var value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
@ -132,20 +123,25 @@ namespace ASC.Common.Caching
|
||||
if (value != null)
|
||||
{
|
||||
dic.AddOrUpdate(field, value, (k, v) => value);
|
||||
MemoryCache.Set(key, dic, DateTime.MaxValue);
|
||||
_memoryCache.Set(key, dic, DateTime.MaxValue);
|
||||
}
|
||||
else if (dic != null)
|
||||
{
|
||||
dic.TryRemove(field, out _);
|
||||
if (dic.Count == 0)
|
||||
|
||||
if (dic.IsEmpty)
|
||||
{
|
||||
MemoryCache.Remove(key);
|
||||
_memoryCache.Remove(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
MemoryCache.Set(key, dic, DateTime.MaxValue);
|
||||
_memoryCache.Set(key, dic, DateTime.MaxValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EvictionCallback(object key, object value, EvictionReason reason, object state)
|
||||
{
|
||||
_memoryCacheKeys.TryRemove(key.ToString(), out _);
|
||||
}
|
||||
}
|
@ -23,8 +23,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Caching
|
||||
{
|
||||
namespace ASC.Common.Caching;
|
||||
|
||||
[Flags]
|
||||
public enum CacheNotifyAction
|
||||
{
|
||||
@ -34,4 +34,3 @@ namespace ASC.Common.Caching
|
||||
InsertOrUpdate = Insert | Update,
|
||||
Any = InsertOrUpdate | Remove,
|
||||
}
|
||||
}
|
||||
|
@ -23,8 +23,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Caching
|
||||
{
|
||||
namespace ASC.Common.Caching;
|
||||
|
||||
[Singletone(typeof(AscCache))]
|
||||
public interface ICache
|
||||
{
|
||||
@ -38,11 +38,9 @@ namespace ASC.Common.Caching
|
||||
|
||||
void Remove(Regex pattern);
|
||||
|
||||
|
||||
ConcurrentDictionary<string, T> HashGetAll<T>(string key);
|
||||
|
||||
T HashGet<T>(string key, string field);
|
||||
|
||||
void HashSet<T>(string key, string field, T value);
|
||||
}
|
||||
}
|
||||
|
@ -23,8 +23,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Caching
|
||||
{
|
||||
namespace ASC.Common.Caching;
|
||||
|
||||
[Singletone]
|
||||
public interface ICacheNotify<T> where T : IMessage<T>, new()
|
||||
{
|
||||
@ -34,4 +34,3 @@ namespace ASC.Common.Caching
|
||||
|
||||
void Unsubscribe(CacheNotifyAction action);
|
||||
}
|
||||
}
|
||||
|
@ -1,199 +0,0 @@
|
||||
namespace ASC.Common.Caching
|
||||
{
|
||||
[Singletone]
|
||||
public class KafkaCache<T> : IDisposable, ICacheNotify<T> where T : IMessage<T>, new()
|
||||
{
|
||||
private ClientConfig ClientConfig { get; set; }
|
||||
private AdminClientConfig AdminClientConfig { get; set; }
|
||||
private ILog Log { get; set; }
|
||||
private ConcurrentDictionary<string, CancellationTokenSource> Cts { get; set; }
|
||||
private ConcurrentDictionary<string, Action<T>> Actions { get; set; }
|
||||
private ProtobufSerializer<T> ValueSerializer { get; } = new ProtobufSerializer<T>();
|
||||
private ProtobufDeserializer<T> ValueDeserializer { get; } = new ProtobufDeserializer<T>();
|
||||
private ProtobufSerializer<AscCacheItem> KeySerializer { get; } = new ProtobufSerializer<AscCacheItem>();
|
||||
private ProtobufDeserializer<AscCacheItem> KeyDeserializer { get; } = new ProtobufDeserializer<AscCacheItem>();
|
||||
private IProducer<AscCacheItem, T> Producer { get; set; }
|
||||
private Guid Key { get; set; }
|
||||
|
||||
public KafkaCache(ConfigurationExtension configuration, IOptionsMonitor<ILog> options)
|
||||
{
|
||||
Log = options.CurrentValue;
|
||||
Cts = new ConcurrentDictionary<string, CancellationTokenSource>();
|
||||
Actions = new ConcurrentDictionary<string, Action<T>>();
|
||||
Key = Guid.NewGuid();
|
||||
|
||||
var settings = configuration.GetSetting<KafkaSettings>("kafka");
|
||||
|
||||
ClientConfig = new ClientConfig { BootstrapServers = settings.BootstrapServers };
|
||||
AdminClientConfig = new AdminClientConfig { BootstrapServers = settings.BootstrapServers };
|
||||
}
|
||||
|
||||
public void Publish(T obj, CacheNotifyAction cacheNotifyAction)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Producer == null)
|
||||
{
|
||||
Producer = new ProducerBuilder<AscCacheItem, T>(new ProducerConfig(ClientConfig))
|
||||
.SetErrorHandler((_, e) => Log.Error(e))
|
||||
.SetKeySerializer(KeySerializer)
|
||||
.SetValueSerializer(ValueSerializer)
|
||||
.Build();
|
||||
}
|
||||
|
||||
var channelName = GetChannelName(cacheNotifyAction);
|
||||
|
||||
if (Actions.TryGetValue(channelName, out var onchange))
|
||||
{
|
||||
onchange(obj);
|
||||
}
|
||||
|
||||
var message = new Message<AscCacheItem, T>
|
||||
{
|
||||
Value = obj,
|
||||
Key = new AscCacheItem
|
||||
{
|
||||
Id = Key.ToString()
|
||||
}
|
||||
};
|
||||
|
||||
Producer.ProduceAsync(channelName, message);
|
||||
}
|
||||
catch (ProduceException<Null, string> e)
|
||||
{
|
||||
Log.Error(e);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void Subscribe(Action<T> onchange, CacheNotifyAction cacheNotifyAction)
|
||||
{
|
||||
var channelName = GetChannelName(cacheNotifyAction);
|
||||
|
||||
Cts[channelName] = new CancellationTokenSource();
|
||||
Actions[channelName] = onchange;
|
||||
|
||||
void action()
|
||||
{
|
||||
var conf = new ConsumerConfig(ClientConfig)
|
||||
{
|
||||
GroupId = Guid.NewGuid().ToString()
|
||||
};
|
||||
|
||||
|
||||
using (var adminClient = new AdminClientBuilder(AdminClientConfig)
|
||||
.SetErrorHandler((_, e) => Log.Error(e))
|
||||
.Build())
|
||||
{
|
||||
try
|
||||
{
|
||||
//TODO: must add checking exist
|
||||
adminClient.CreateTopicsAsync(
|
||||
new TopicSpecification[]
|
||||
{
|
||||
new TopicSpecification
|
||||
{
|
||||
Name = channelName,
|
||||
NumPartitions = 1,
|
||||
ReplicationFactor = 1
|
||||
}
|
||||
}).Wait();
|
||||
}
|
||||
catch (AggregateException)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
using var c = new ConsumerBuilder<AscCacheItem, T>(conf)
|
||||
.SetErrorHandler((_, e) => Log.Error(e))
|
||||
.SetKeyDeserializer(KeyDeserializer)
|
||||
.SetValueDeserializer(ValueDeserializer)
|
||||
.Build();
|
||||
|
||||
c.Assign(new TopicPartition(channelName, new Partition()));
|
||||
|
||||
try
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
var cr = c.Consume(Cts[channelName].Token);
|
||||
if (cr != null && cr.Message != null && cr.Message.Value != null && !(new Guid(cr.Message.Key.Id)).Equals(Key) && Actions.TryGetValue(channelName, out var act))
|
||||
{
|
||||
try
|
||||
{
|
||||
act(cr.Message.Value);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error("Kafka onmessage", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ConsumeException e)
|
||||
{
|
||||
Log.Error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
c.Close();
|
||||
}
|
||||
}
|
||||
|
||||
var task = new Task(action, TaskCreationOptions.LongRunning);
|
||||
task.Start();
|
||||
}
|
||||
|
||||
private string GetChannelName(CacheNotifyAction cacheNotifyAction)
|
||||
{
|
||||
return $"ascchannel{cacheNotifyAction}{typeof(T).FullName}".ToLower();
|
||||
}
|
||||
|
||||
public void Unsubscribe(CacheNotifyAction action)
|
||||
{
|
||||
Cts.TryGetValue(GetChannelName(action), out var source);
|
||||
if (source != null)
|
||||
{
|
||||
source.Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
private bool disposedValue = false; // To detect redundant calls
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposedValue)
|
||||
{
|
||||
if (disposing && Producer != null)
|
||||
{
|
||||
Producer.Dispose();
|
||||
}
|
||||
|
||||
disposedValue = true;
|
||||
}
|
||||
}
|
||||
~KafkaCache()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
public class KafkaSettings
|
||||
{
|
||||
public string BootstrapServers { get; set; }
|
||||
}
|
||||
}
|
196
common/ASC.Common/Caching/KafkaCacheNotify.cs
Normal file
196
common/ASC.Common/Caching/KafkaCacheNotify.cs
Normal file
@ -0,0 +1,196 @@
|
||||
namespace ASC.Common.Caching;
|
||||
|
||||
[Singletone]
|
||||
public class KafkaCacheNotify<T> : IDisposable, ICacheNotify<T> where T : IMessage<T>, new()
|
||||
{
|
||||
private IProducer<AscCacheItem, T> _producer;
|
||||
|
||||
private bool _disposedValue = false; // To detect redundant calls
|
||||
private readonly ClientConfig _clientConfig;
|
||||
private readonly AdminClientConfig _adminClientConfig;
|
||||
private readonly ILog _logger;
|
||||
private readonly ConcurrentDictionary<string, CancellationTokenSource> _cancelationToken;
|
||||
private readonly ConcurrentDictionary<string, Action<T>> _actions;
|
||||
private readonly ProtobufSerializer<T> _valueSerializer = new ProtobufSerializer<T>();
|
||||
private readonly ProtobufDeserializer<T> _valueDeserializer = new ProtobufDeserializer<T>();
|
||||
private readonly ProtobufSerializer<AscCacheItem> _keySerializer = new ProtobufSerializer<AscCacheItem>();
|
||||
private readonly ProtobufDeserializer<AscCacheItem> _keyDeserializer = new ProtobufDeserializer<AscCacheItem>();
|
||||
private readonly Guid _key;
|
||||
|
||||
public KafkaCacheNotify(ConfigurationExtension configuration, IOptionsMonitor<ILog> options)
|
||||
{
|
||||
_logger = options.CurrentValue;
|
||||
_cancelationToken = new ConcurrentDictionary<string, CancellationTokenSource>();
|
||||
_actions = new ConcurrentDictionary<string, Action<T>>();
|
||||
_key = Guid.NewGuid();
|
||||
|
||||
var settings = configuration.GetSetting<KafkaSettings>("kafka");
|
||||
|
||||
_clientConfig = new ClientConfig { BootstrapServers = settings.BootstrapServers };
|
||||
_adminClientConfig = new AdminClientConfig { BootstrapServers = settings.BootstrapServers };
|
||||
}
|
||||
|
||||
public void Publish(T obj, CacheNotifyAction notifyAction)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_producer == null)
|
||||
{
|
||||
_producer = new ProducerBuilder<AscCacheItem, T>(new ProducerConfig(_clientConfig))
|
||||
.SetErrorHandler((_, e) => _logger.Error(e))
|
||||
.SetKeySerializer(_keySerializer)
|
||||
.SetValueSerializer(_valueSerializer)
|
||||
.Build();
|
||||
}
|
||||
|
||||
var channelName = GetChannelName(notifyAction);
|
||||
|
||||
if (_actions.TryGetValue(channelName, out var onchange))
|
||||
{
|
||||
onchange(obj);
|
||||
}
|
||||
|
||||
var message = new Message<AscCacheItem, T>
|
||||
{
|
||||
Value = obj,
|
||||
Key = new AscCacheItem
|
||||
{
|
||||
Id = _key.ToString()
|
||||
}
|
||||
};
|
||||
|
||||
_producer.ProduceAsync(channelName, message);
|
||||
}
|
||||
catch (ProduceException<Null, string> e)
|
||||
{
|
||||
_logger.Error(e);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void Subscribe(Action<T> onchange, CacheNotifyAction notifyAction)
|
||||
{
|
||||
var channelName = GetChannelName(notifyAction);
|
||||
|
||||
_cancelationToken[channelName] = new CancellationTokenSource();
|
||||
_actions[channelName] = onchange;
|
||||
|
||||
void action()
|
||||
{
|
||||
var conf = new ConsumerConfig(_clientConfig)
|
||||
{
|
||||
GroupId = Guid.NewGuid().ToString()
|
||||
};
|
||||
|
||||
|
||||
using (var adminClient = new AdminClientBuilder(_adminClientConfig)
|
||||
.SetErrorHandler((_, e) => _logger.Error(e))
|
||||
.Build())
|
||||
{
|
||||
try
|
||||
{
|
||||
//TODO: must add checking exist
|
||||
adminClient.CreateTopicsAsync(
|
||||
new TopicSpecification[]
|
||||
{
|
||||
new TopicSpecification
|
||||
{
|
||||
Name = channelName,
|
||||
NumPartitions = 1,
|
||||
ReplicationFactor = 1
|
||||
}
|
||||
}).Wait();
|
||||
}
|
||||
catch (AggregateException) { }
|
||||
}
|
||||
|
||||
using var c = new ConsumerBuilder<AscCacheItem, T>(conf)
|
||||
.SetErrorHandler((_, e) => _logger.Error(e))
|
||||
.SetKeyDeserializer(_keyDeserializer)
|
||||
.SetValueDeserializer(_valueDeserializer)
|
||||
.Build();
|
||||
|
||||
c.Assign(new TopicPartition(channelName, new Partition()));
|
||||
|
||||
try
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
var cr = c.Consume(_cancelationToken[channelName].Token);
|
||||
if (cr != null && cr.Message != null && cr.Message.Value != null && !(new Guid(cr.Message.Key.Id)).Equals(_key) && _actions.TryGetValue(channelName, out var act))
|
||||
{
|
||||
try
|
||||
{
|
||||
act(cr.Message.Value);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error("Kafka onmessage", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ConsumeException e)
|
||||
{
|
||||
_logger.Error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
c.Close();
|
||||
}
|
||||
}
|
||||
|
||||
var task = new Task(action, TaskCreationOptions.LongRunning);
|
||||
task.Start();
|
||||
}
|
||||
|
||||
public void Unsubscribe(CacheNotifyAction notifyAction)
|
||||
{
|
||||
_cancelationToken.TryGetValue(GetChannelName(notifyAction), out var source);
|
||||
|
||||
if (source != null)
|
||||
{
|
||||
source.Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposedValue)
|
||||
{
|
||||
if (disposing && _producer != null)
|
||||
{
|
||||
_producer.Dispose();
|
||||
}
|
||||
|
||||
_disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
~KafkaCacheNotify()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
private string GetChannelName(CacheNotifyAction notifyAction)
|
||||
{
|
||||
return $"ascchannel{notifyAction}{typeof(T).FullName}".ToLower();
|
||||
}
|
||||
}
|
||||
|
||||
public class KafkaSettings
|
||||
{
|
||||
public string BootstrapServers { get; set; }
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
namespace ASC.Common.Caching
|
||||
{
|
||||
namespace ASC.Common.Caching;
|
||||
|
||||
[Singletone]
|
||||
public class MemoryCacheNotify<T> : ICacheNotify<T> where T : IMessage<T>, new()
|
||||
{
|
||||
@ -10,9 +10,9 @@
|
||||
_actions = new ConcurrentDictionary<string, List<Action<T>>>();
|
||||
}
|
||||
|
||||
public void Publish(T obj, CacheNotifyAction action)
|
||||
public void Publish(T obj, CacheNotifyAction notifyAction)
|
||||
{
|
||||
if (_actions.TryGetValue(GetKey(action), out var onchange) && onchange != null)
|
||||
if (_actions.TryGetValue(GetKey(notifyAction), out var onchange) && onchange != null)
|
||||
{
|
||||
Parallel.ForEach(onchange, a => a(obj));
|
||||
}
|
||||
@ -27,14 +27,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
public void Unsubscribe(CacheNotifyAction action)
|
||||
public void Unsubscribe(CacheNotifyAction notifyAction)
|
||||
{
|
||||
_actions.TryRemove(GetKey(action), out _);
|
||||
_actions.TryRemove(GetKey(notifyAction), out _);
|
||||
}
|
||||
|
||||
private string GetKey(CacheNotifyAction cacheNotifyAction)
|
||||
private string GetKey(CacheNotifyAction notifyAction)
|
||||
{
|
||||
return $"asc:channel:{cacheNotifyAction}:{typeof(T).FullName}".ToLower();
|
||||
}
|
||||
return $"asc:channel:{notifyAction}:{typeof(T).FullName}".ToLower();
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
namespace ASC.Common.Caching
|
||||
{
|
||||
namespace ASC.Common.Caching;
|
||||
|
||||
public class ProtobufSerializer<T> : ISerializer<T> where T : IMessage<T>, new()
|
||||
{
|
||||
public byte[] Serialize(T data, SerializationContext context)
|
||||
@ -10,28 +10,22 @@
|
||||
|
||||
public class ProtobufDeserializer<T> : IDeserializer<T> where T : IMessage<T>, new()
|
||||
{
|
||||
private readonly MessageParser<T> parser;
|
||||
private readonly MessageParser<T> _parser;
|
||||
|
||||
public ProtobufDeserializer()
|
||||
{
|
||||
parser = new MessageParser<T>(() => new T());
|
||||
_parser = new MessageParser<T>(() => new T());
|
||||
}
|
||||
|
||||
public T Deserialize(ReadOnlySpan<byte> data, bool isNull, SerializationContext context)
|
||||
{
|
||||
return parser.ParseFrom(data.ToArray());
|
||||
return _parser.ParseFrom(data.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
public static class GuidExtension
|
||||
{
|
||||
public static ByteString ToByteString(this Guid id)
|
||||
{
|
||||
return ByteString.CopyFrom(id.ToByteArray());
|
||||
}
|
||||
public static Guid FromByteString(this ByteString id)
|
||||
{
|
||||
return new Guid(id.ToByteArray());
|
||||
}
|
||||
}
|
||||
public static ByteString ToByteString(this Guid id) => ByteString.CopyFrom(id.ToByteArray());
|
||||
|
||||
public static Guid FromByteString(this ByteString id) => new Guid(id.ToByteArray());
|
||||
}
|
@ -17,11 +17,11 @@
|
||||
namespace ASC.Common.Caching;
|
||||
|
||||
[Singletone]
|
||||
public class RedisCache<T> : ICacheNotify<T> where T : IMessage<T>, new()
|
||||
public class RedisCacheNotify<T> : ICacheNotify<T> where T : IMessage<T>, new()
|
||||
{
|
||||
private readonly IRedisDatabase _redis;
|
||||
|
||||
public RedisCache(IRedisCacheClient redisCacheClient)
|
||||
public RedisCacheNotify(IRedisCacheClient redisCacheClient)
|
||||
{
|
||||
_redis = redisCacheClient.GetDbFromConfiguration();
|
||||
}
|
||||
@ -53,9 +53,9 @@ public class RedisCache<T> : ICacheNotify<T> where T : IMessage<T>, new()
|
||||
.GetResult();
|
||||
}
|
||||
|
||||
private string GetChannelName(CacheNotifyAction cacheNotifyAction)
|
||||
private string GetChannelName(CacheNotifyAction action)
|
||||
{
|
||||
return $"asc:channel:{cacheNotifyAction}:{typeof(T).FullName}".ToLower();
|
||||
return $"asc:channel:{action}:{typeof(T).FullName}".ToLower();
|
||||
}
|
||||
|
||||
class RedisCachePubSubItem<T0>
|
||||
@ -65,6 +65,3 @@ public class RedisCache<T> : ICacheNotify<T> where T : IMessage<T>, new()
|
||||
public CacheNotifyAction Action { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -23,28 +23,22 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Collections
|
||||
{
|
||||
namespace ASC.Collections;
|
||||
|
||||
public abstract class CachedDictionaryBase<T>
|
||||
{
|
||||
protected string baseKey;
|
||||
protected Func<T, bool> condition;
|
||||
protected string BaseKey { get; set; }
|
||||
protected Func<T, bool> Condition { get; set; }
|
||||
|
||||
public T this[string key]
|
||||
{
|
||||
get { return Get(key); }
|
||||
}
|
||||
public T this[string key] => Get(key);
|
||||
|
||||
public T this[Func<T> @default]
|
||||
{
|
||||
get { return Get(@default); }
|
||||
}
|
||||
public T this[Func<T> @default] => Get(@default);
|
||||
|
||||
protected abstract void InsertRootKey(string rootKey);
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
InsertRootKey(baseKey);
|
||||
InsertRootKey(BaseKey);
|
||||
}
|
||||
|
||||
public void Clear(string rootKey)
|
||||
@ -57,8 +51,6 @@ namespace ASC.Collections
|
||||
Reset(string.Empty, key);
|
||||
}
|
||||
|
||||
public abstract void Reset(string rootKey, string key);
|
||||
|
||||
public T Get(string key)
|
||||
{
|
||||
return Get(string.Empty, key, null);
|
||||
@ -69,26 +61,16 @@ namespace ASC.Collections
|
||||
return Get(string.Empty, key, defaults);
|
||||
}
|
||||
|
||||
|
||||
public void Add(string key, T newValue)
|
||||
{
|
||||
Add(string.Empty, key, newValue);
|
||||
}
|
||||
|
||||
public abstract void Add(string rootkey, string key, T newValue);
|
||||
|
||||
public bool HasItem(string key)
|
||||
{
|
||||
return !Equals(Get(key), default(T));
|
||||
}
|
||||
|
||||
protected string BuildKey(string key, string rootkey)
|
||||
{
|
||||
return $"{baseKey}-{rootkey}-{key}";
|
||||
}
|
||||
|
||||
protected abstract object GetObjectFromCache(string fullKey);
|
||||
|
||||
public T Get(Func<T> @default)
|
||||
{
|
||||
var key = string.Format("func {0} {2}.{1}({3})", @default.Method.ReturnType, @default.Method.Name,
|
||||
@ -99,38 +81,53 @@ namespace ASC.Collections
|
||||
return Get(key, @default);
|
||||
}
|
||||
|
||||
protected virtual bool FitsCondition(object cached)
|
||||
{
|
||||
return cached is T;
|
||||
}
|
||||
|
||||
public virtual T Get(string rootkey, string key, Func<T> defaults)
|
||||
{
|
||||
var fullKey = BuildKey(key, rootkey);
|
||||
var objectCache = GetObjectFromCache(fullKey);
|
||||
|
||||
if (FitsCondition(objectCache))
|
||||
{
|
||||
OnHit(fullKey);
|
||||
|
||||
return ReturnCached(objectCache);
|
||||
}
|
||||
|
||||
if (defaults != null)
|
||||
{
|
||||
OnMiss(fullKey);
|
||||
var newValue = defaults();
|
||||
if (condition == null || condition(newValue))
|
||||
{
|
||||
|
||||
if (Condition == null || Condition(newValue))
|
||||
Add(rootkey, key, newValue);
|
||||
}
|
||||
|
||||
return newValue;
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
public abstract void Add(string rootkey, string key, T newValue);
|
||||
|
||||
public abstract void Reset(string rootKey, string key);
|
||||
|
||||
protected virtual bool FitsCondition(object cached)
|
||||
{
|
||||
return cached is T;
|
||||
}
|
||||
|
||||
protected virtual T ReturnCached(object objectCache)
|
||||
{
|
||||
return (T)objectCache;
|
||||
}
|
||||
|
||||
protected string BuildKey(string key, string rootkey)
|
||||
{
|
||||
return $"{BaseKey}-{rootkey}-{key}";
|
||||
}
|
||||
|
||||
protected abstract object GetObjectFromCache(string fullKey);
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
protected virtual void OnHit(string fullKey)
|
||||
{
|
||||
@ -143,4 +140,3 @@ namespace ASC.Collections
|
||||
Debug.Print("cache miss:{0}", fullKey);
|
||||
}
|
||||
}
|
||||
}
|
@ -23,10 +23,61 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Collections
|
||||
{
|
||||
namespace ASC.Collections;
|
||||
|
||||
public sealed class HttpRequestDictionary<T> : CachedDictionaryBase<T>
|
||||
{
|
||||
private readonly HttpContext _httpContext;
|
||||
|
||||
public HttpRequestDictionary(HttpContext httpContext, string baseKey)
|
||||
{
|
||||
Condition = (T) => true;
|
||||
BaseKey = baseKey;
|
||||
_httpContext = httpContext;
|
||||
}
|
||||
|
||||
public override void Reset(string rootKey, string key)
|
||||
{
|
||||
if (_httpContext != null)
|
||||
{
|
||||
var builtkey = BuildKey(key, rootKey);
|
||||
_httpContext.Items[builtkey] = null;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Add(string rootkey, string key, T newValue)
|
||||
{
|
||||
if (_httpContext != null)
|
||||
{
|
||||
var builtkey = BuildKey(key, rootkey);
|
||||
_httpContext.Items[builtkey] = new CachedItem(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
protected override object GetObjectFromCache(string fullKey)
|
||||
{
|
||||
return _httpContext?.Items[fullKey];
|
||||
}
|
||||
|
||||
protected override bool FitsCondition(object cached)
|
||||
{
|
||||
return cached is CachedItem;
|
||||
}
|
||||
|
||||
protected override T ReturnCached(object objectCache)
|
||||
{
|
||||
return ((CachedItem)objectCache).Value;
|
||||
}
|
||||
|
||||
protected override void OnHit(string fullKey) { }
|
||||
|
||||
protected override void OnMiss(string fullKey) { }
|
||||
|
||||
protected override void InsertRootKey(string rootKey)
|
||||
{
|
||||
//We can't expire in HtppContext in such way
|
||||
}
|
||||
|
||||
private sealed class CachedItem
|
||||
{
|
||||
internal T Value { get; set; }
|
||||
@ -36,60 +87,4 @@ namespace ASC.Collections
|
||||
Value = value;
|
||||
}
|
||||
}
|
||||
|
||||
HttpContext HttpContext { get; set; }
|
||||
|
||||
public HttpRequestDictionary(HttpContext httpContext, string baseKey)
|
||||
{
|
||||
condition = (T) => true;
|
||||
this.baseKey = baseKey;
|
||||
HttpContext = httpContext;
|
||||
}
|
||||
|
||||
protected override void InsertRootKey(string rootKey)
|
||||
{
|
||||
//We can't expire in HtppContext in such way
|
||||
}
|
||||
|
||||
public override void Reset(string rootKey, string key)
|
||||
{
|
||||
if (HttpContext != null)
|
||||
{
|
||||
var builtkey = BuildKey(key, rootKey);
|
||||
HttpContext.Items[builtkey] = null;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Add(string rootkey, string key, T newValue)
|
||||
{
|
||||
if (HttpContext != null)
|
||||
{
|
||||
var builtkey = BuildKey(key, rootkey);
|
||||
HttpContext.Items[builtkey] = new CachedItem(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
protected override object GetObjectFromCache(string fullKey)
|
||||
{
|
||||
return HttpContext?.Items[fullKey];
|
||||
}
|
||||
|
||||
protected override bool FitsCondition(object cached)
|
||||
{
|
||||
return cached is CachedItem;
|
||||
}
|
||||
protected override T ReturnCached(object objectCache)
|
||||
{
|
||||
return ((CachedItem)objectCache).Value;
|
||||
}
|
||||
|
||||
protected override void OnHit(string fullKey)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnMiss(string fullKey)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
namespace ASC.Common
|
||||
{
|
||||
namespace ASC.Common;
|
||||
|
||||
public enum DIAttributeEnum
|
||||
{
|
||||
Singletone,
|
||||
@ -9,7 +9,7 @@
|
||||
|
||||
public class TransientAttribute : DIAttribute
|
||||
{
|
||||
public override DIAttributeEnum DIAttributeEnum { get => DIAttributeEnum.Transient; }
|
||||
public override DIAttributeEnum DIAttributeEnum => DIAttributeEnum.Transient;
|
||||
|
||||
public TransientAttribute() { }
|
||||
|
||||
@ -32,7 +32,7 @@
|
||||
|
||||
public class ScopeAttribute : DIAttribute
|
||||
{
|
||||
public override DIAttributeEnum DIAttributeEnum { get => DIAttributeEnum.Scope; }
|
||||
public override DIAttributeEnum DIAttributeEnum => DIAttributeEnum.Scope;
|
||||
|
||||
public ScopeAttribute() { }
|
||||
|
||||
@ -55,7 +55,7 @@
|
||||
|
||||
public class SingletoneAttribute : DIAttribute
|
||||
{
|
||||
public override DIAttributeEnum DIAttributeEnum { get => DIAttributeEnum.Singletone; }
|
||||
public override DIAttributeEnum DIAttributeEnum => DIAttributeEnum.Singletone;
|
||||
|
||||
public SingletoneAttribute() { }
|
||||
|
||||
@ -114,6 +114,7 @@
|
||||
{ DIAttributeEnum.Scope, new List<string>() },
|
||||
{ DIAttributeEnum.Transient, new List<string>() }
|
||||
};
|
||||
|
||||
Added = new List<string>();
|
||||
Configured = new List<string>();
|
||||
}
|
||||
@ -157,6 +158,7 @@
|
||||
))
|
||||
{
|
||||
service = service.GetGenericArguments().FirstOrDefault();
|
||||
|
||||
if (service == null)
|
||||
{
|
||||
return false;
|
||||
@ -164,7 +166,12 @@
|
||||
}
|
||||
|
||||
var serviceName = $"{service}{implementation}";
|
||||
if (Added.Contains(serviceName)) return false;
|
||||
|
||||
if (Added.Contains(serviceName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Added.Add(serviceName);
|
||||
|
||||
var di = service.IsGenericType && (
|
||||
@ -172,6 +179,7 @@
|
||||
service.GetGenericTypeDefinition() == typeof(IPostConfigureOptions<>) ||
|
||||
service.GetGenericTypeDefinition() == typeof(IOptionsMonitor<>)
|
||||
) && implementation != null ? implementation.GetCustomAttribute<DIAttribute>() : service.GetCustomAttribute<DIAttribute>();
|
||||
|
||||
var isnew = false;
|
||||
|
||||
if (di != null)
|
||||
@ -185,7 +193,10 @@
|
||||
if (!service.IsInterface || implementation != null)
|
||||
{
|
||||
isnew = implementation != null ? Register(service, implementation) : Register(service);
|
||||
if (!isnew) return false;
|
||||
if (!isnew)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (service.IsInterface && implementation == null || !service.IsInterface)
|
||||
@ -197,6 +208,7 @@
|
||||
x.GetGenericTypeDefinition() == typeof(IPostConfigureOptions<>) ||
|
||||
x.GetGenericTypeDefinition() == typeof(IOptionsMonitor<>)
|
||||
));
|
||||
|
||||
if (a != null)
|
||||
{
|
||||
if (!a.ContainsGenericParameters)
|
||||
@ -208,6 +220,7 @@
|
||||
if (g != service)
|
||||
{
|
||||
TryAdd(g);
|
||||
|
||||
if (service.IsInterface && di.Implementation == null)
|
||||
{
|
||||
TryAdd(service, g);
|
||||
@ -250,7 +263,6 @@
|
||||
{
|
||||
Register(di.Service);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -303,6 +315,7 @@
|
||||
//a, di.Service
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
isnew = TryAdd(service, di.Implementation);
|
||||
@ -331,6 +344,7 @@
|
||||
if (props != null)
|
||||
{
|
||||
var par = props.SelectMany(r => r.GetParameters()).Distinct();
|
||||
|
||||
foreach (var p1 in par)
|
||||
{
|
||||
TryAdd(p1.ParameterType);
|
||||
@ -341,29 +355,10 @@
|
||||
return isnew;
|
||||
}
|
||||
|
||||
private bool Register(Type service, Type implementation = null)
|
||||
{
|
||||
if (service.IsSubclassOf(typeof(ControllerBase)) || service.GetInterfaces().Contains(typeof(IResourceFilter)) || service.GetInterfaces().Contains(typeof(IDictionary<string, string>))) return true;
|
||||
var c = service.IsGenericType && (
|
||||
service.GetGenericTypeDefinition() == typeof(IConfigureOptions<>) ||
|
||||
service.GetGenericTypeDefinition() == typeof(IPostConfigureOptions<>) ||
|
||||
service.GetGenericTypeDefinition() == typeof(IOptionsMonitor<>)
|
||||
) && implementation != null ? implementation.GetCustomAttribute<DIAttribute>() : service.GetCustomAttribute<DIAttribute>();
|
||||
var serviceName = $"{service}{implementation}";
|
||||
|
||||
if (!Services[c.DIAttributeEnum].Contains(serviceName))
|
||||
{
|
||||
c.TryAdd(ServiceCollection, service, implementation);
|
||||
Services[c.DIAttributeEnum].Add(serviceName);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public DIHelper TryAddSingleton<TService>(Func<IServiceProvider, TService> implementationFactory) where TService : class
|
||||
{
|
||||
var serviceName = $"{typeof(TService)}";
|
||||
|
||||
if (!Services[DIAttributeEnum.Singletone].Contains(serviceName))
|
||||
{
|
||||
Services[DIAttributeEnum.Singletone].Add(serviceName);
|
||||
@ -376,6 +371,7 @@
|
||||
public DIHelper TryAddSingleton<TService, TImplementation>() where TService : class where TImplementation : class, TService
|
||||
{
|
||||
var serviceName = $"{typeof(TService)}{typeof(TImplementation)}";
|
||||
|
||||
if (!Services[DIAttributeEnum.Singletone].Contains(serviceName))
|
||||
{
|
||||
Services[DIAttributeEnum.Singletone].Add(serviceName);
|
||||
@ -388,6 +384,7 @@
|
||||
public DIHelper Configure<TOptions>(Action<TOptions> configureOptions) where TOptions : class
|
||||
{
|
||||
var serviceName = $"{typeof(TOptions)}";
|
||||
|
||||
if (!Configured.Contains(serviceName))
|
||||
{
|
||||
Configured.Add(serviceName);
|
||||
@ -400,6 +397,7 @@
|
||||
public DIHelper Configure<TOptions>(string name, Action<TOptions> configureOptions) where TOptions : class
|
||||
{
|
||||
var serviceName = $"{typeof(TOptions)}{name}";
|
||||
|
||||
if (!Configured.Contains(serviceName))
|
||||
{
|
||||
Configured.Add(serviceName);
|
||||
@ -408,5 +406,31 @@
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private bool Register(Type service, Type implementation = null)
|
||||
{
|
||||
if (service.IsSubclassOf(typeof(ControllerBase)) || service.GetInterfaces().Contains(typeof(IResourceFilter))
|
||||
|| service.GetInterfaces().Contains(typeof(IDictionary<string, string>)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var c = service.IsGenericType && (
|
||||
service.GetGenericTypeDefinition() == typeof(IConfigureOptions<>) ||
|
||||
service.GetGenericTypeDefinition() == typeof(IPostConfigureOptions<>) ||
|
||||
service.GetGenericTypeDefinition() == typeof(IOptionsMonitor<>)
|
||||
) && implementation != null ? implementation.GetCustomAttribute<DIAttribute>() : service.GetCustomAttribute<DIAttribute>();
|
||||
|
||||
var serviceName = $"{service}{implementation}";
|
||||
|
||||
if (!Services[c.DIAttributeEnum].Contains(serviceName))
|
||||
{
|
||||
c.TryAdd(ServiceCollection, service, implementation);
|
||||
Services[c.DIAttributeEnum].Add(serviceName);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -23,9 +23,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Data;
|
||||
|
||||
namespace ASC.Common.Data
|
||||
{
|
||||
public static class StreamExtension
|
||||
{
|
||||
public const int BufferSize = 2048; //NOTE: set to 2048 to fit in minimum tcp window
|
||||
@ -38,6 +37,7 @@ namespace ASC.Common.Data
|
||||
var buffer = new byte[BufferSize];
|
||||
int totalRead = 0;
|
||||
int readed;
|
||||
|
||||
while ((readed = srcStream.Read(buffer, 0, length - totalRead > BufferSize ? BufferSize : length - totalRead)) > 0 && totalRead < length)
|
||||
{
|
||||
dstStream.Write(buffer, 0, readed);
|
||||
@ -45,4 +45,3 @@ namespace ASC.Common.Data
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -14,38 +14,36 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace System.IO
|
||||
{
|
||||
namespace System.IO;
|
||||
|
||||
[Singletone]
|
||||
public class TempPath
|
||||
{
|
||||
readonly string tempFolder;
|
||||
private readonly string _tempFolder;
|
||||
|
||||
public TempPath(IConfiguration configuration)
|
||||
{
|
||||
string rootFolder = AppContext.BaseDirectory;
|
||||
|
||||
if (string.IsNullOrEmpty(rootFolder))
|
||||
{
|
||||
rootFolder = Assembly.GetEntryAssembly().Location;
|
||||
}
|
||||
|
||||
tempFolder = configuration["temp"] ?? Path.Combine("..", "Data", "temp");
|
||||
|
||||
if (!Path.IsPathRooted(tempFolder))
|
||||
_tempFolder = configuration["temp"] ?? Path.Combine("..", "Data", "temp");
|
||||
if (!Path.IsPathRooted(_tempFolder))
|
||||
{
|
||||
tempFolder = Path.GetFullPath(Path.Combine(rootFolder, tempFolder));
|
||||
_tempFolder = Path.GetFullPath(Path.Combine(rootFolder, _tempFolder));
|
||||
}
|
||||
|
||||
if (!Directory.Exists(tempFolder))
|
||||
if (!Directory.Exists(_tempFolder))
|
||||
{
|
||||
Directory.CreateDirectory(tempFolder);
|
||||
Directory.CreateDirectory(_tempFolder);
|
||||
}
|
||||
}
|
||||
|
||||
public string GetTempPath()
|
||||
{
|
||||
return tempFolder;
|
||||
return _tempFolder;
|
||||
}
|
||||
|
||||
public string GetTempFileName()
|
||||
@ -56,7 +54,7 @@ namespace System.IO
|
||||
|
||||
do
|
||||
{
|
||||
path = Path.Combine(tempFolder, Path.GetRandomFileName());
|
||||
path = Path.Combine(_tempFolder, Path.GetRandomFileName());
|
||||
|
||||
try
|
||||
{
|
||||
@ -68,16 +66,19 @@ namespace System.IO
|
||||
catch (IOException ex)
|
||||
{
|
||||
if (ex.HResult != -2147024816 || count++ > 65536)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
catch (UnauthorizedAccessException ex)
|
||||
{
|
||||
if (count++ > 65536)
|
||||
{
|
||||
throw new IOException(ex.Message, ex);
|
||||
}
|
||||
}
|
||||
} while (f == null);
|
||||
|
||||
return path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,16 +14,16 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common
|
||||
{
|
||||
namespace ASC.Common;
|
||||
|
||||
[Singletone]
|
||||
public class TempStream
|
||||
{
|
||||
private TempPath TempPath { get; }
|
||||
private readonly TempPath _tempPath;
|
||||
|
||||
public TempStream(TempPath tempPath)
|
||||
{
|
||||
TempPath = tempPath;
|
||||
_tempPath = tempPath;
|
||||
}
|
||||
|
||||
public Stream GetBuffered(Stream srcStream)
|
||||
@ -35,14 +35,16 @@ namespace ASC.Common
|
||||
var memStream = Create();
|
||||
srcStream.CopyTo(memStream);
|
||||
memStream.Position = 0;
|
||||
|
||||
return memStream;
|
||||
}
|
||||
|
||||
return srcStream;
|
||||
}
|
||||
|
||||
public Stream Create()
|
||||
{
|
||||
return new FileStream(TempPath.GetTempFileName(), FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read, 4096, FileOptions.DeleteOnClose);
|
||||
}
|
||||
return new FileStream(_tempPath.GetTempFileName(), FileMode.OpenOrCreate,
|
||||
FileAccess.ReadWrite, FileShare.Read, 4096, FileOptions.DeleteOnClose);
|
||||
}
|
||||
}
|
@ -1,10 +1,11 @@
|
||||
namespace ASC.Common.DependencyInjection
|
||||
{
|
||||
namespace ASC.Common.DependencyInjection;
|
||||
|
||||
internal class AutofacComponent
|
||||
{
|
||||
public string Type { get; set; }
|
||||
public IEnumerable<AutofacService> Services { get; set; }
|
||||
}
|
||||
|
||||
internal class AutofacService
|
||||
{
|
||||
public string Type { get; set; }
|
||||
@ -12,7 +13,8 @@
|
||||
|
||||
public static class AutofacExtension
|
||||
{
|
||||
public static void Register(this ContainerBuilder builder, IConfiguration configuration, bool loadproducts = true, bool loadconsumers = true, params string[] intern)
|
||||
public static void Register(this ContainerBuilder builder, IConfiguration configuration,
|
||||
bool loadproducts = true, bool loadconsumers = true, params string[] intern)
|
||||
{
|
||||
var modules = new List<(bool, string)>
|
||||
{
|
||||
@ -37,10 +39,12 @@
|
||||
foreach (var p in modules)
|
||||
{
|
||||
var config = new ConfigurationBuilder();
|
||||
|
||||
if (p.Item1)
|
||||
{
|
||||
config.SetBasePath(configuration["pathToConf"]);
|
||||
}
|
||||
|
||||
config.AddJsonFile(p.Item2);
|
||||
|
||||
var root = config.Build();
|
||||
@ -59,7 +63,6 @@
|
||||
var root = config.Build();
|
||||
|
||||
var sectionSettings = root.GetSection("components");
|
||||
|
||||
if (sectionSettings == null)
|
||||
{
|
||||
return new List<string>();
|
||||
@ -119,36 +122,41 @@
|
||||
string GetFullPath(string n)
|
||||
{
|
||||
var productPath = CrossPlatform.PathCombine(productsDir, n, subfolder);
|
||||
return GetPath(CrossPlatform.PathCombine(productPath, "bin"), n, SearchOption.AllDirectories) ?? GetPath(productPath, n, SearchOption.TopDirectoryOnly);
|
||||
|
||||
return GetPath(CrossPlatform.PathCombine(productPath, "bin"), n, SearchOption.AllDirectories)
|
||||
?? GetPath(productPath, n, SearchOption.TopDirectoryOnly);
|
||||
}
|
||||
|
||||
static string GetPath(string dirPath, string dll, SearchOption searchOption)
|
||||
{
|
||||
if (!Directory.Exists(dirPath)) return null;
|
||||
if (!Directory.Exists(dirPath))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return Directory.GetFiles(dirPath, $"{dll}.dll", searchOption).FirstOrDefault();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
class Resolver
|
||||
{
|
||||
private string ResolvePath { get; set; }
|
||||
private readonly string _resolvePath;
|
||||
|
||||
public Resolver(string assemblyPath)
|
||||
{
|
||||
ResolvePath = assemblyPath;
|
||||
_resolvePath = assemblyPath;
|
||||
}
|
||||
|
||||
public Assembly Resolving(AssemblyLoadContext context, AssemblyName assemblyName)
|
||||
{
|
||||
var path = CrossPlatform.PathCombine(Path.GetDirectoryName(ResolvePath), $"{assemblyName.Name}.dll");
|
||||
var path = CrossPlatform.PathCombine(Path.GetDirectoryName(_resolvePath), $"{assemblyName.Name}.dll");
|
||||
|
||||
if (!File.Exists(path)) return null;
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return context.LoadFromAssemblyPath(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,23 +23,17 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Geolocation
|
||||
{
|
||||
namespace ASC.Geolocation;
|
||||
|
||||
public class IPGeolocationInfo
|
||||
{
|
||||
public string Key { get; set; }
|
||||
|
||||
public string City { get; set; }
|
||||
|
||||
public double TimezoneOffset { get; set; }
|
||||
|
||||
public string TimezoneName { get; set; }
|
||||
|
||||
public string IPStart { get; set; }
|
||||
|
||||
public string IPEnd { get; set; }
|
||||
|
||||
|
||||
public readonly static IPGeolocationInfo Default = new IPGeolocationInfo
|
||||
{
|
||||
Key = string.Empty,
|
||||
@ -49,4 +43,3 @@ namespace ASC.Geolocation
|
||||
TimezoneName = string.Empty,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,8 @@ global using System.Threading.Tasks;
|
||||
global using System.Web;
|
||||
global using System.Xml.Linq;
|
||||
global using System.Xml.XPath;
|
||||
global using System.ServiceModel;
|
||||
global using System.Runtime.Serialization;
|
||||
|
||||
global using ARSoft.Tools.Net;
|
||||
global using ARSoft.Tools.Net.Dns;
|
||||
|
@ -1,18 +1,18 @@
|
||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||
using LogLevel = Microsoft.Extensions.Logging.LogLevel;
|
||||
|
||||
namespace ASC.Common.Logging
|
||||
{
|
||||
namespace ASC.Common.Logging;
|
||||
|
||||
[Singletone]
|
||||
public class EFLoggerFactory : ILoggerFactory
|
||||
{
|
||||
Lazy<ILogger> Logger { get; set; }
|
||||
ILoggerProvider LoggerProvider { get; set; }
|
||||
private readonly Lazy<ILogger> _logger;
|
||||
private readonly ILoggerProvider _loggerProvider;
|
||||
|
||||
public EFLoggerFactory(EFLoggerProvider loggerProvider)
|
||||
{
|
||||
LoggerProvider = loggerProvider;
|
||||
Logger = new Lazy<ILogger>(() => LoggerProvider.CreateLogger(""));
|
||||
_loggerProvider = loggerProvider;
|
||||
_logger = new Lazy<ILogger>(() => _loggerProvider.CreateLogger(""));
|
||||
}
|
||||
|
||||
public void AddProvider(ILoggerProvider provider)
|
||||
@ -22,27 +22,25 @@ namespace ASC.Common.Logging
|
||||
|
||||
public ILogger CreateLogger(string categoryName)
|
||||
{
|
||||
return Logger.Value;
|
||||
return _logger.Value;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
public void Dispose() { }
|
||||
}
|
||||
|
||||
[Singletone]
|
||||
public class EFLoggerProvider : ILoggerProvider
|
||||
{
|
||||
private IOptionsMonitor<ILog> Option { get; }
|
||||
private readonly IOptionsMonitor<ILog> _option;
|
||||
|
||||
public EFLoggerProvider(IOptionsMonitor<ILog> option)
|
||||
{
|
||||
Option = option;
|
||||
_option = option;
|
||||
}
|
||||
|
||||
public ILogger CreateLogger(string categoryName)
|
||||
{
|
||||
return new EFLogger(Option.Get("ASC.SQL"));
|
||||
return new EFLogger(_option.Get("ASC.SQL"));
|
||||
}
|
||||
|
||||
public void Dispose() { }
|
||||
@ -57,7 +55,11 @@ namespace ASC.Common.Logging
|
||||
CustomLogger = customLogger;
|
||||
}
|
||||
|
||||
public IDisposable BeginScope<TState>(TState state) { return null; }
|
||||
public IDisposable BeginScope<TState>(TState state)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool IsEnabled(LogLevel logLevel)
|
||||
{
|
||||
return logLevel switch
|
||||
@ -89,4 +91,3 @@ namespace ASC.Common.Logging
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -23,67 +23,13 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Logging
|
||||
{
|
||||
namespace ASC.Common.Logging;
|
||||
|
||||
public class SelfCleaningAppender : RollingFileAppender
|
||||
{
|
||||
private static DateTime _lastCleanDate;
|
||||
|
||||
private static int? _cleanPeriod;
|
||||
|
||||
private static int GetCleanPeriod()
|
||||
{
|
||||
if (_cleanPeriod != null)
|
||||
return _cleanPeriod.Value;
|
||||
|
||||
const string key = "CleanPeriod";
|
||||
|
||||
var value = 30;
|
||||
|
||||
var repo = log4net.LogManager.GetRepository(Assembly.GetCallingAssembly());
|
||||
|
||||
if (repo != null && repo.Properties.GetKeys().Contains(key))
|
||||
{
|
||||
int.TryParse(repo.Properties[key].ToString(), out value);
|
||||
}
|
||||
|
||||
_cleanPeriod = value;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private void Clean()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(File))
|
||||
return;
|
||||
|
||||
var fileInfo = new FileInfo(File);
|
||||
|
||||
if (!fileInfo.Exists)
|
||||
return;
|
||||
|
||||
var directory = fileInfo.Directory;
|
||||
|
||||
if (directory == null || !directory.Exists)
|
||||
return;
|
||||
|
||||
var files = directory.GetFiles();
|
||||
|
||||
var cleanPeriod = GetCleanPeriod();
|
||||
|
||||
foreach (var file in files.Where(file => (DateTime.UtcNow.Date - file.CreationTimeUtc.Date).Days > cleanPeriod))
|
||||
{
|
||||
file.Delete();
|
||||
}
|
||||
}
|
||||
catch (Exception err)
|
||||
{
|
||||
LogLog.Error(GetType(), err.Message, err);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Append(LoggingEvent loggingEvent)
|
||||
{
|
||||
if (DateTime.UtcNow.Date > _lastCleanDate.Date)
|
||||
@ -105,5 +51,61 @@ namespace ASC.Common.Logging
|
||||
|
||||
base.Append(loggingEvents);
|
||||
}
|
||||
|
||||
private static int GetCleanPeriod()
|
||||
{
|
||||
if (_cleanPeriod != null)
|
||||
{
|
||||
return _cleanPeriod.Value;
|
||||
}
|
||||
|
||||
const string key = "CleanPeriod";
|
||||
|
||||
var value = 30;
|
||||
|
||||
var repo = log4net.LogManager.GetRepository(Assembly.GetCallingAssembly());
|
||||
if (repo != null && repo.Properties.GetKeys().Contains(key))
|
||||
{
|
||||
int.TryParse(repo.Properties[key].ToString(), out value);
|
||||
}
|
||||
|
||||
_cleanPeriod = value;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private void Clean()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(File))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var fileInfo = new FileInfo(File);
|
||||
if (!fileInfo.Exists)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var directory = fileInfo.Directory;
|
||||
if (directory == null || !directory.Exists)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var files = directory.GetFiles();
|
||||
var cleanPeriod = GetCleanPeriod();
|
||||
|
||||
foreach (var file in files.Where(file => (DateTime.UtcNow.Date - file.CreationTimeUtc.Date).Days > cleanPeriod))
|
||||
{
|
||||
file.Delete();
|
||||
}
|
||||
}
|
||||
catch (Exception err)
|
||||
{
|
||||
LogLog.Error(GetType(), err.Message, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,19 +25,56 @@
|
||||
|
||||
using LogLevel = NLog.LogLevel;
|
||||
|
||||
namespace ASC.Common.Logging
|
||||
{
|
||||
namespace ASC.Common.Logging;
|
||||
|
||||
[Target("SelfCleaning")]
|
||||
public class SelfCleaningTarget : FileTarget
|
||||
{
|
||||
private static DateTime _lastCleanDate;
|
||||
|
||||
private static int? _cleanPeriod;
|
||||
|
||||
protected override void Write(IList<AsyncLogEventInfo> logEvents)
|
||||
{
|
||||
if (DateTime.UtcNow.Date > _lastCleanDate.Date)
|
||||
{
|
||||
_lastCleanDate = DateTime.UtcNow.Date;
|
||||
Clean();
|
||||
}
|
||||
|
||||
var buffer = new List<AsyncLogEventInfo>();
|
||||
|
||||
foreach (var logEvent in logEvents)
|
||||
{
|
||||
buffer.Add(logEvent);
|
||||
if (buffer.Count < 10)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
base.Write(buffer);
|
||||
buffer.Clear();
|
||||
}
|
||||
|
||||
base.Write(buffer);
|
||||
}
|
||||
|
||||
protected override void Write(LogEventInfo logEvent)
|
||||
{
|
||||
if (DateTime.UtcNow.Date > _lastCleanDate.Date)
|
||||
{
|
||||
_lastCleanDate = DateTime.UtcNow.Date;
|
||||
Clean();
|
||||
}
|
||||
|
||||
base.Write(logEvent);
|
||||
}
|
||||
|
||||
private static int GetCleanPeriod()
|
||||
{
|
||||
if (_cleanPeriod != null)
|
||||
{
|
||||
return _cleanPeriod.Value;
|
||||
}
|
||||
|
||||
var value = 30;
|
||||
|
||||
@ -64,28 +101,33 @@ namespace ASC.Common.Logging
|
||||
try
|
||||
{
|
||||
if (FileName == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
filePath = ((NLog.Layouts.SimpleLayout)FileName).Text;
|
||||
|
||||
if (string.IsNullOrEmpty(filePath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
dirPath = Path.GetDirectoryName(filePath);
|
||||
|
||||
if (string.IsNullOrEmpty(dirPath))
|
||||
{
|
||||
return;
|
||||
|
||||
}
|
||||
if (!Path.IsPathRooted(dirPath))
|
||||
{
|
||||
dirPath = CrossPlatform.PathCombine(AppDomain.CurrentDomain.BaseDirectory, dirPath);
|
||||
}
|
||||
|
||||
var directory = new DirectoryInfo(dirPath);
|
||||
|
||||
if (!directory.Exists)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var files = directory.GetFiles();
|
||||
|
||||
var cleanPeriod = GetCleanPeriod();
|
||||
|
||||
foreach (var file in files.Where(file => (DateTime.UtcNow.Date - file.CreationTimeUtc.Date).Days > cleanPeriod))
|
||||
@ -104,37 +146,4 @@ namespace ASC.Common.Logging
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Write(IList<AsyncLogEventInfo> logEvents)
|
||||
{
|
||||
if (DateTime.UtcNow.Date > _lastCleanDate.Date)
|
||||
{
|
||||
_lastCleanDate = DateTime.UtcNow.Date;
|
||||
Clean();
|
||||
}
|
||||
|
||||
var buffer = new List<AsyncLogEventInfo>();
|
||||
|
||||
foreach (var logEvent in logEvents)
|
||||
{
|
||||
buffer.Add(logEvent);
|
||||
if (buffer.Count < 10) continue;
|
||||
base.Write(buffer);
|
||||
buffer.Clear();
|
||||
}
|
||||
|
||||
base.Write(buffer);
|
||||
}
|
||||
|
||||
protected override void Write(LogEventInfo logEvent)
|
||||
{
|
||||
if (DateTime.UtcNow.Date > _lastCleanDate.Date)
|
||||
{
|
||||
_lastCleanDate = DateTime.UtcNow.Date;
|
||||
Clean();
|
||||
}
|
||||
|
||||
base.Write(logEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,8 +23,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Logging
|
||||
{
|
||||
namespace ASC.Common.Logging;
|
||||
|
||||
public class SpecialFolderPathConverter : PatternConverter
|
||||
{
|
||||
protected override void Convert(TextWriter writer, object state)
|
||||
@ -33,10 +33,12 @@ namespace ASC.Common.Logging
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var result = string.Empty;
|
||||
const string CMD_LINE = "CommandLine:";
|
||||
|
||||
if (Option.StartsWith(CMD_LINE))
|
||||
{
|
||||
var args = Environment.CommandLine.Split(' ');
|
||||
@ -60,6 +62,7 @@ namespace ASC.Common.Logging
|
||||
{
|
||||
realKey = "UNIX:" + Option;
|
||||
}
|
||||
|
||||
if (Path.DirectorySeparatorChar == '\\' && key == "WINDOWS:" + Option)
|
||||
{
|
||||
realKey = "WINDOWS:" + Option;
|
||||
@ -91,4 +94,3 @@ namespace ASC.Common.Logging
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,10 +23,12 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Mapping
|
||||
{
|
||||
namespace ASC.Common.Mapping;
|
||||
|
||||
public interface IMapFrom<T>
|
||||
{
|
||||
void Mapping(Profile profile) => profile.CreateMap(typeof(T), GetType());
|
||||
void Mapping(Profile profile)
|
||||
{
|
||||
profile.CreateMap(typeof(T), GetType());
|
||||
}
|
||||
}
|
||||
|
@ -23,18 +23,18 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Mapping
|
||||
{
|
||||
namespace ASC.Common.Mapping;
|
||||
|
||||
public class MappingProfile : Profile
|
||||
{
|
||||
public MappingProfile()
|
||||
{
|
||||
Array.ForEach(AppDomain.CurrentDomain.GetAssemblies(), a => ApplyMappingsFromAssembly(a));
|
||||
}
|
||||
public MappingProfile() => Array.ForEach(AppDomain.CurrentDomain.GetAssemblies(), a => ApplyMappingsFromAssembly(a));
|
||||
|
||||
private void ApplyMappingsFromAssembly(Assembly assembly)
|
||||
{
|
||||
if (!assembly.GetName().Name.StartsWith("ASC.")) return;
|
||||
if (!assembly.GetName().Name.StartsWith("ASC."))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var types = assembly.GetExportedTypes()
|
||||
.Where(t => t.GetInterfaces().Any(i =>
|
||||
@ -48,8 +48,6 @@ namespace ASC.Common.Mapping
|
||||
?? type.GetInterface("IMapFrom`1").GetMethod("Mapping");
|
||||
|
||||
methodInfo?.Invoke(instance, new object[] { this });
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -23,16 +23,11 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Module;
|
||||
|
||||
using System.ServiceModel;
|
||||
|
||||
namespace ASC.Common.Module
|
||||
{
|
||||
public class BaseWcfClient<TService> : ClientBase<TService>, IDisposable where TService : class
|
||||
{
|
||||
public BaseWcfClient()
|
||||
{
|
||||
}
|
||||
public BaseWcfClient() { }
|
||||
|
||||
void IDisposable.Dispose()
|
||||
{
|
||||
@ -56,4 +51,3 @@ namespace ASC.Common.Module
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,12 +24,11 @@
|
||||
*/
|
||||
|
||||
|
||||
namespace ASC.Common.Module
|
||||
{
|
||||
namespace ASC.Common.Module;
|
||||
|
||||
public interface IServiceController
|
||||
{
|
||||
void Start();
|
||||
|
||||
void Stop();
|
||||
}
|
||||
}
|
||||
|
@ -1,192 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* (c) Copyright Ascensio System Limited 2010-2018
|
||||
*
|
||||
* This program is freeware. You can redistribute it and/or modify it under the terms of the GNU
|
||||
* General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html).
|
||||
* In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that
|
||||
* Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights.
|
||||
*
|
||||
* THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
* You can contact Ascensio System SIA by email at sales@onlyoffice.com
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display
|
||||
* Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3.
|
||||
*
|
||||
* Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains
|
||||
* relevant author attributions when distributing the software. If the display of the logo in its graphic
|
||||
* form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE"
|
||||
* in every copy of the program you distribute.
|
||||
* Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
using System;
|
||||
using System.Configuration;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Mail;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using ASC.Common.Utils;
|
||||
using Amazon.SimpleEmail;
|
||||
using Amazon.SimpleEmail.Model;
|
||||
using log4net;
|
||||
|
||||
namespace ASC.Common.Notify
|
||||
{
|
||||
public class AWSEmail
|
||||
{
|
||||
public enum SendStatus
|
||||
{
|
||||
Ok,
|
||||
Failed,
|
||||
QuotaLimit
|
||||
}
|
||||
|
||||
private readonly AmazonSimpleEmailServiceClient _emailService;
|
||||
private readonly ILog _log = LogHolder.Log("ASC.Notify.AmazonSES");
|
||||
|
||||
//Static fields
|
||||
private static readonly TimeSpan RefreshTimeout;
|
||||
private static DateTime _lastRefresh;
|
||||
private static DateTime _lastSend;
|
||||
private static TimeSpan _sendWindow = TimeSpan.MinValue;
|
||||
private static GetSendQuotaResult _quota;
|
||||
private static readonly object SynchRoot = new object();
|
||||
|
||||
|
||||
static AWSEmail()
|
||||
{
|
||||
RefreshTimeout = TimeSpan.FromMinutes(30);
|
||||
if (!string.IsNullOrEmpty(ConfigurationManager.AppSettings["ses.refreshTimeout"]))
|
||||
{
|
||||
TimeSpan.TryParse(ConfigurationManager.AppSettings["ses.refreshTimeout"], out RefreshTimeout);
|
||||
}
|
||||
_lastRefresh = DateTime.UtcNow - RefreshTimeout;//Set to refresh on first send
|
||||
}
|
||||
|
||||
public AWSEmail()
|
||||
{
|
||||
var accessKey = ConfigurationManager.AppSettings["ses.accessKey"];
|
||||
var secretKey = ConfigurationManager.AppSettings["ses.secretKey"];
|
||||
|
||||
_emailService = new AmazonSimpleEmailServiceClient(accessKey, secretKey);
|
||||
|
||||
|
||||
}
|
||||
|
||||
public SendStatus SendEmail(MailMessage mailMessage)
|
||||
{
|
||||
//Check if we need to query stats
|
||||
RefreshQuotaIfNeeded();
|
||||
if (_quota != null)
|
||||
{
|
||||
lock (SynchRoot)
|
||||
{
|
||||
if (_quota.Max24HourSend <= _quota.SentLast24Hours)
|
||||
{
|
||||
//Quota exceeded
|
||||
//Queu next refresh to +24 hours
|
||||
_lastRefresh = DateTime.UtcNow.AddHours(24);
|
||||
_log.WarnFormat("quota limit reached. setting next check to: {0}", _lastRefresh);
|
||||
return SendStatus.QuotaLimit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
var destination = new Destination
|
||||
{
|
||||
ToAddresses = mailMessage.To.Select(adresses => adresses.Address).ToList(),
|
||||
BccAddresses = mailMessage.Bcc.Select(adresses => adresses.Address).ToList(),
|
||||
CcAddresses = mailMessage.CC.Select(adresses => adresses.Address).ToList(),
|
||||
|
||||
};
|
||||
|
||||
var body = new Body(new Content(mailMessage.Body) { Charset = Encoding.UTF8.WebName });
|
||||
|
||||
if (mailMessage.AlternateViews.Count > 0)
|
||||
{
|
||||
//Get html body
|
||||
foreach (var alternateView in mailMessage.AlternateViews)
|
||||
{
|
||||
if ("text/html".Equals(alternateView.ContentType.MediaType, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var stream = alternateView.ContentStream;
|
||||
var buf = new byte[stream.Length];
|
||||
stream.Read(buf, 0, buf.Length);
|
||||
stream.Seek(0, SeekOrigin.Begin);//NOTE:seek to begin to keep HTML body
|
||||
body.Html = new Content(Encoding.UTF8.GetString(buf)) { Charset = Encoding.UTF8.WebName };
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var message = new Message(new Content(mailMessage.Subject), body);
|
||||
|
||||
var seRequest = new SendEmailRequest(mailMessage.From.ToEncodedStringEx(), destination, message);
|
||||
if (mailMessage.ReplyTo != null)
|
||||
seRequest.ReplyToAddresses.Add(mailMessage.ReplyTo.Address);
|
||||
|
||||
ThrottleIfNeeded();
|
||||
|
||||
var response = _emailService.SendEmail(seRequest);
|
||||
_lastSend = DateTime.UtcNow;
|
||||
return response != null ? SendStatus.Ok : SendStatus.Failed;
|
||||
}
|
||||
|
||||
private void ThrottleIfNeeded()
|
||||
{
|
||||
//Check last send and throttle if needed
|
||||
if (_sendWindow != TimeSpan.MinValue)
|
||||
{
|
||||
if (DateTime.UtcNow - _lastSend <= _sendWindow)
|
||||
//Possible BUG: at high frequncies maybe bug with to little differences
|
||||
{
|
||||
//This means that time passed from last send is less then message per second
|
||||
_log.DebugFormat("send rate doesn't fit in send window. sleeping for:{0}", _sendWindow);
|
||||
Thread.Sleep(_sendWindow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshQuotaIfNeeded()
|
||||
{
|
||||
if (!IsRefreshNeeded()) return;
|
||||
|
||||
lock (SynchRoot)
|
||||
{
|
||||
if (IsRefreshNeeded())//Double check
|
||||
{
|
||||
_log.DebugFormat("refreshing qouta. interval: {0} Last refresh was at: {1}", RefreshTimeout,
|
||||
_lastRefresh);
|
||||
|
||||
//Do quota refresh
|
||||
_lastRefresh = DateTime.UtcNow.AddMinutes(1);
|
||||
try
|
||||
{
|
||||
var quotaRequest = new GetSendQuotaRequest();
|
||||
_quota = _emailService.GetSendQuota(quotaRequest).GetSendQuotaResult;
|
||||
_sendWindow = TimeSpan.FromSeconds(1.0 / _quota.MaxSendRate);
|
||||
_log.DebugFormat("quota: {0}/{1} at {2} mps. send window:{3}", _quota.SentLast24Hours,
|
||||
_quota.Max24HourSend, _quota.MaxSendRate, _sendWindow);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_log.Error("error refreshing quota", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsRefreshNeeded()
|
||||
{
|
||||
return (DateTime.UtcNow - _lastRefresh) > RefreshTimeout || _quota == null;
|
||||
}
|
||||
}
|
||||
}
|
@ -23,51 +23,55 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Security
|
||||
{
|
||||
namespace ASC.Common.Security;
|
||||
|
||||
public class AscRandom : Random
|
||||
{
|
||||
private const int Mbig = int.MaxValue;
|
||||
private const int Mseed = 161803398;
|
||||
private const int Mz = 0;
|
||||
|
||||
private int _inext;
|
||||
private int _inextp;
|
||||
private const int MBIG = int.MaxValue;
|
||||
private const int MSEED = 161803398;
|
||||
private const int MZ = 0;
|
||||
private readonly int[] seeds;
|
||||
private readonly int[] _seeds;
|
||||
|
||||
|
||||
public AscRandom() : this(Environment.TickCount)
|
||||
{
|
||||
}
|
||||
public AscRandom() : this(Environment.TickCount) { }
|
||||
|
||||
public AscRandom(int seed)
|
||||
{
|
||||
seeds = new int[56];
|
||||
_seeds = new int[56];
|
||||
var num4 = (seed == int.MinValue) ? int.MaxValue : Math.Abs(seed);
|
||||
var num2 = 161803398 - num4;
|
||||
seeds[^1] = num2;
|
||||
_seeds[^1] = num2;
|
||||
var num3 = 1;
|
||||
for (var i = 1; i < seeds.Length - 1; i++)
|
||||
|
||||
for (var i = 1; i < _seeds.Length - 1; i++)
|
||||
{
|
||||
var index = 21 * i % (seeds.Length - 1);
|
||||
seeds[index] = num3;
|
||||
var index = 21 * i % (_seeds.Length - 1);
|
||||
_seeds[index] = num3;
|
||||
num3 = num2 - num3;
|
||||
|
||||
if (num3 < 0)
|
||||
{
|
||||
num3 += int.MaxValue;
|
||||
}
|
||||
num2 = seeds[index];
|
||||
|
||||
num2 = _seeds[index];
|
||||
}
|
||||
|
||||
for (var j = 1; j < 5; j++)
|
||||
{
|
||||
for (var k = 1; k < seeds.Length; k++)
|
||||
for (var k = 1; k < _seeds.Length; k++)
|
||||
{
|
||||
seeds[k] -= seeds[1 + ((k + 30) % (seeds.Length - 1))];
|
||||
if (seeds[k] < 0)
|
||||
_seeds[k] -= _seeds[1 + ((k + 30) % (_seeds.Length - 1))];
|
||||
|
||||
if (_seeds[k] < 0)
|
||||
{
|
||||
seeds[k] += int.MaxValue;
|
||||
_seeds[k] += int.MaxValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_inext = 0;
|
||||
_inextp = 21;
|
||||
}
|
||||
@ -78,12 +82,16 @@ namespace ASC.Common.Security
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(maxValue));
|
||||
}
|
||||
|
||||
return (int)(InternalSample() * 4.6566128752457969E-10 * maxValue);
|
||||
}
|
||||
|
||||
public override void NextBytes(byte[] buffer)
|
||||
{
|
||||
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
|
||||
if (buffer == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(buffer));
|
||||
}
|
||||
|
||||
for (var i = 0; i < buffer.Length; i++)
|
||||
{
|
||||
@ -93,29 +101,35 @@ namespace ASC.Common.Security
|
||||
|
||||
private int InternalSample()
|
||||
{
|
||||
var inext = this._inext;
|
||||
var inextp = this._inextp;
|
||||
if (++inext >= seeds.Length - 1)
|
||||
var inext = _inext;
|
||||
var inextp = _inextp;
|
||||
|
||||
if (++inext >= _seeds.Length - 1)
|
||||
{
|
||||
inext = 1;
|
||||
}
|
||||
if (++inextp >= seeds.Length - 1)
|
||||
|
||||
if (++inextp >= _seeds.Length - 1)
|
||||
{
|
||||
inextp = 1;
|
||||
}
|
||||
var num = seeds[inext] - seeds[inextp];
|
||||
|
||||
var num = _seeds[inext] - _seeds[inextp];
|
||||
|
||||
if (num == int.MaxValue)
|
||||
{
|
||||
num--;
|
||||
}
|
||||
|
||||
if (num < 0)
|
||||
{
|
||||
num += int.MaxValue;
|
||||
}
|
||||
seeds[inext] = num;
|
||||
this._inext = inext;
|
||||
this._inextp = inextp;
|
||||
|
||||
_seeds[inext] = num;
|
||||
_inext = inext;
|
||||
_inextp = inextp;
|
||||
|
||||
return num;
|
||||
}
|
||||
}
|
||||
}
|
@ -23,11 +23,16 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Security.Authentication
|
||||
{
|
||||
namespace ASC.Common.Security.Authentication;
|
||||
|
||||
[Serializable]
|
||||
public class Account : IAccount
|
||||
{
|
||||
public Guid ID { get; private set; }
|
||||
public string Name { get; private set; }
|
||||
public virtual bool IsAuthenticated { get; private set; }
|
||||
public string AuthenticationType => "ASC";
|
||||
|
||||
public Account(Guid id, string name, bool authenticated)
|
||||
{
|
||||
ID = id;
|
||||
@ -35,31 +40,11 @@ namespace ASC.Common.Security.Authentication
|
||||
IsAuthenticated = authenticated;
|
||||
}
|
||||
|
||||
#region IAccount Members
|
||||
|
||||
public Guid ID { get; private set; }
|
||||
|
||||
public string Name { get; private set; }
|
||||
|
||||
|
||||
public object Clone()
|
||||
{
|
||||
return MemberwiseClone();
|
||||
}
|
||||
|
||||
public string AuthenticationType
|
||||
{
|
||||
get { return "ASC"; }
|
||||
}
|
||||
|
||||
public virtual bool IsAuthenticated
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is IAccount a && ID.Equals(a.ID);
|
||||
@ -75,4 +60,3 @@ namespace ASC.Common.Security.Authentication
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
}
|
@ -23,10 +23,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Security.Authentication
|
||||
{
|
||||
public interface IAccount : ISubject, ICloneable
|
||||
{
|
||||
namespace ASC.Common.Security.Authentication;
|
||||
|
||||
}
|
||||
}
|
||||
public interface IAccount : ISubject, ICloneable { }
|
||||
|
@ -24,9 +24,6 @@
|
||||
*/
|
||||
|
||||
|
||||
namespace ASC.Common.Security.Authentication
|
||||
{
|
||||
public interface ISystemAccount : IAccount
|
||||
{
|
||||
}
|
||||
}
|
||||
namespace ASC.Common.Security.Authentication;
|
||||
|
||||
public interface ISystemAccount : IAccount { }
|
||||
|
@ -24,16 +24,13 @@
|
||||
*/
|
||||
|
||||
|
||||
namespace ASC.Common.Security.Authentication
|
||||
{
|
||||
namespace ASC.Common.Security.Authentication;
|
||||
|
||||
public interface IUserAccount : IAccount
|
||||
{
|
||||
string Email { get; }
|
||||
string FirstName { get; }
|
||||
|
||||
string LastName { get; }
|
||||
|
||||
string Title { get; }
|
||||
int Tenant { get; }
|
||||
}
|
||||
}
|
@ -23,14 +23,11 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Security.Authentication
|
||||
{
|
||||
namespace ASC.Common.Security.Authentication;
|
||||
|
||||
[Serializable]
|
||||
public class SystemAccount : Account, ISystemAccount
|
||||
{
|
||||
public SystemAccount(Guid id, string name, bool authenticated)
|
||||
: base(id, name, authenticated)
|
||||
{
|
||||
}
|
||||
}
|
||||
: base(id, name, authenticated) { }
|
||||
}
|
@ -24,64 +24,55 @@
|
||||
*/
|
||||
|
||||
|
||||
#region usings
|
||||
namespace ASC.Common.Security.Authorizing;
|
||||
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace ASC.Common.Security.Authorizing
|
||||
{
|
||||
[Serializable]
|
||||
public class AuthorizingException : Exception
|
||||
{
|
||||
private readonly string _Message;
|
||||
public ISubject Subject { get; internal set; }
|
||||
public IAction[] Actions { get; internal set; }
|
||||
public override string Message => _message;
|
||||
|
||||
private readonly string _message;
|
||||
|
||||
public AuthorizingException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
: base(message) { }
|
||||
|
||||
public AuthorizingException(ISubject subject, IAction[] actions)
|
||||
{
|
||||
if (actions == null || actions.Length == 0) throw new ArgumentNullException(nameof(actions));
|
||||
if (actions == null || actions.Length == 0)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(actions));
|
||||
}
|
||||
|
||||
Subject = subject ?? throw new ArgumentNullException(nameof(subject));
|
||||
Actions = actions;
|
||||
var sactions = "";
|
||||
|
||||
Array.ForEach(actions, action => { sactions += action.ToString() + ", "; });
|
||||
_Message = string.Format(
|
||||
|
||||
_message = string.Format(
|
||||
"\"{0}\" access denied \"{1}\"",
|
||||
subject,
|
||||
sactions
|
||||
);
|
||||
}
|
||||
|
||||
public AuthorizingException(ISubject subject, IAction[] actions, ISubject[] denySubjects, IAction[] denyActions)
|
||||
{
|
||||
_Message = FormatErrorMessage(subject, actions, denySubjects, denyActions);
|
||||
}
|
||||
public AuthorizingException(ISubject subject, IAction[] actions, ISubject[] denySubjects, IAction[] denyActions) =>
|
||||
_message = FormatErrorMessage(subject, actions, denySubjects, denyActions);
|
||||
|
||||
protected AuthorizingException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{
|
||||
_Message = info.GetValue("_Message", typeof(string)) as string;
|
||||
_message = info.GetValue("_Message", typeof(string)) as string;
|
||||
Subject = info.GetValue("Subject", typeof(ISubject)) as ISubject;
|
||||
Actions = info.GetValue("Actions", typeof(IAction[])) as IAction[];
|
||||
}
|
||||
|
||||
public override string Message
|
||||
{
|
||||
get { return _Message; }
|
||||
}
|
||||
|
||||
public ISubject Subject { get; internal set; }
|
||||
public IAction[] Actions { get; internal set; }
|
||||
|
||||
public override void GetObjectData(SerializationInfo info, StreamingContext context)
|
||||
{
|
||||
info.AddValue("Subject", Subject, typeof(ISubject));
|
||||
info.AddValue("_Message", _Message, typeof(string));
|
||||
info.AddValue("_Message", _message, typeof(string));
|
||||
info.AddValue("Actions", Actions, typeof(IAction[]));
|
||||
base.GetObjectData(info, context);
|
||||
}
|
||||
@ -89,12 +80,26 @@ namespace ASC.Common.Security.Authorizing
|
||||
internal static string FormatErrorMessage(ISubject subject, IAction[] actions, ISubject[] denySubjects,
|
||||
IAction[] denyActions)
|
||||
{
|
||||
if (subject == null) throw new ArgumentNullException(nameof(subject));
|
||||
if (actions == null || actions.Length == 0) throw new ArgumentNullException(nameof(actions));
|
||||
if (denySubjects == null || denySubjects.Length == 0) throw new ArgumentNullException(nameof(denySubjects));
|
||||
if (denyActions == null || denyActions.Length == 0) throw new ArgumentNullException(nameof(denyActions));
|
||||
if (subject == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(subject));
|
||||
}
|
||||
if (actions == null || actions.Length == 0)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(actions));
|
||||
}
|
||||
if (denySubjects == null || denySubjects.Length == 0)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(denySubjects));
|
||||
}
|
||||
if (denyActions == null || denyActions.Length == 0)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(denyActions));
|
||||
}
|
||||
if (actions.Length != denySubjects.Length || actions.Length != denyActions.Length)
|
||||
{
|
||||
throw new ArgumentException();
|
||||
}
|
||||
|
||||
var sb = new StringBuilder();
|
||||
for (var i = 0; i < actions.Length; i++)
|
||||
@ -105,18 +110,25 @@ namespace ASC.Common.Security.Authorizing
|
||||
|
||||
string reason;
|
||||
if (denySubject != null && denyAction != null)
|
||||
{
|
||||
reason = $"{action.Name}:{(denySubject is IRole ? "role:" : "") + denySubject.Name} access denied {denyAction.Name}.";
|
||||
}
|
||||
else
|
||||
{
|
||||
reason = $"{action.Name}: access denied.";
|
||||
}
|
||||
if (i != actions.Length - 1)
|
||||
{
|
||||
reason += ", ";
|
||||
}
|
||||
|
||||
sb.Append(reason);
|
||||
}
|
||||
var reasons = sb.ToString();
|
||||
var sactions = "";
|
||||
Array.ForEach(actions, action => { sactions += action.ToString() + ", "; });
|
||||
|
||||
var message = $"\"{(subject is IRole ? "role:" : "") + subject.Name}\" access denied \"{sactions}\". Cause: {reasons}.";
|
||||
return message;
|
||||
}
|
||||
}
|
||||
}
|
@ -24,16 +24,19 @@
|
||||
*/
|
||||
|
||||
|
||||
namespace ASC.Common.Security.Authorizing
|
||||
{
|
||||
namespace ASC.Common.Security.Authorizing;
|
||||
|
||||
public static class AzObjectIdHelper
|
||||
{
|
||||
private static readonly string separator = "|";
|
||||
private static readonly string _separator = "|";
|
||||
|
||||
public static string GetFullObjectId(ISecurityObjectId objectId)
|
||||
{
|
||||
if (objectId == null) return null;
|
||||
return string.Format("{0}{1}{2}", objectId.ObjectType.FullName, separator, objectId.SecurityId);
|
||||
}
|
||||
if (objectId == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return $"{objectId.ObjectType.FullName}{_separator}{objectId.SecurityId}";
|
||||
}
|
||||
}
|
@ -23,61 +23,67 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Security.Authorizing;
|
||||
|
||||
#region usings
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
namespace ASC.Common.Security.Authorizing
|
||||
{
|
||||
public class AzObjectSecurityProviderHelper
|
||||
{
|
||||
private readonly SecurityCallContext callContext;
|
||||
private readonly bool currObjIdAsProvider;
|
||||
private ISecurityObjectProvider currSecObjProvider;
|
||||
public ISecurityObjectId CurrentObjectId { get; private set; }
|
||||
public bool ObjectRolesSupported => _currSecObjProvider != null && _currSecObjProvider.ObjectRolesSupported;
|
||||
|
||||
private readonly SecurityCallContext _callContext;
|
||||
private readonly bool _currObjIdAsProvider;
|
||||
private ISecurityObjectProvider _currSecObjProvider;
|
||||
|
||||
public AzObjectSecurityProviderHelper(ISecurityObjectId objectId, ISecurityObjectProvider secObjProvider)
|
||||
{
|
||||
currObjIdAsProvider = false;
|
||||
_currObjIdAsProvider = false;
|
||||
CurrentObjectId = objectId ?? throw new ArgumentNullException(nameof(objectId));
|
||||
currSecObjProvider = secObjProvider;
|
||||
if (currSecObjProvider == null && CurrentObjectId is ISecurityObjectProvider securityObjectProvider)
|
||||
_currSecObjProvider = secObjProvider;
|
||||
|
||||
if (_currSecObjProvider == null && CurrentObjectId is ISecurityObjectProvider securityObjectProvider)
|
||||
{
|
||||
currObjIdAsProvider = true;
|
||||
currSecObjProvider = securityObjectProvider;
|
||||
}
|
||||
callContext = new SecurityCallContext();
|
||||
_currObjIdAsProvider = true;
|
||||
_currSecObjProvider = securityObjectProvider;
|
||||
}
|
||||
|
||||
public ISecurityObjectId CurrentObjectId { get; private set; }
|
||||
|
||||
public bool ObjectRolesSupported
|
||||
{
|
||||
get { return currSecObjProvider != null && currSecObjProvider.ObjectRolesSupported; }
|
||||
_callContext = new SecurityCallContext();
|
||||
}
|
||||
|
||||
public IEnumerable<IRole> GetObjectRoles(ISubject account)
|
||||
{
|
||||
var roles = currSecObjProvider.GetObjectRoles(account, CurrentObjectId, callContext);
|
||||
var roles = _currSecObjProvider.GetObjectRoles(account, CurrentObjectId, _callContext);
|
||||
|
||||
foreach (var role in roles)
|
||||
{
|
||||
if (!callContext.RolesList.Contains(role)) callContext.RolesList.Add(role);
|
||||
if (!_callContext.RolesList.Contains(role))
|
||||
{
|
||||
_callContext.RolesList.Add(role);
|
||||
}
|
||||
}
|
||||
|
||||
return roles;
|
||||
}
|
||||
|
||||
public bool NextInherit()
|
||||
{
|
||||
if (currSecObjProvider == null || !currSecObjProvider.InheritSupported) return false;
|
||||
CurrentObjectId = currSecObjProvider.InheritFrom(CurrentObjectId);
|
||||
if (CurrentObjectId == null) return false;
|
||||
if (currObjIdAsProvider)
|
||||
if (_currSecObjProvider == null || !_currSecObjProvider.InheritSupported)
|
||||
{
|
||||
currSecObjProvider = CurrentObjectId as ISecurityObjectProvider;
|
||||
}
|
||||
callContext.ObjectsStack.Insert(0, CurrentObjectId);
|
||||
return currSecObjProvider != null;
|
||||
return false;
|
||||
}
|
||||
|
||||
CurrentObjectId = _currSecObjProvider.InheritFrom(CurrentObjectId);
|
||||
if (CurrentObjectId == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_currObjIdAsProvider)
|
||||
{
|
||||
_currSecObjProvider = CurrentObjectId as ISecurityObjectProvider;
|
||||
}
|
||||
|
||||
_callContext.ObjectsStack.Insert(0, CurrentObjectId);
|
||||
|
||||
return _currSecObjProvider != null;
|
||||
}
|
||||
}
|
@ -23,8 +23,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Security.Authorizing
|
||||
{
|
||||
namespace ASC.Common.Security.Authorizing;
|
||||
|
||||
public static class Constants
|
||||
{
|
||||
public static readonly Role Admin = new Role(new Guid("cd84e66b-b803-40fc-99f9-b2969a54a1de"), "Admin");
|
||||
@ -43,4 +43,3 @@ namespace ASC.Common.Security.Authorizing
|
||||
|
||||
public static readonly Role Self = new Role(new Guid("5d5b7260-f7f7-49f1-a1c9-95fbb6a12604"), "Self");
|
||||
}
|
||||
}
|
@ -23,20 +23,17 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Security.Authorizing
|
||||
{
|
||||
namespace ASC.Common.Security.Authorizing;
|
||||
|
||||
[Serializable]
|
||||
public class Ace
|
||||
{
|
||||
public Guid ActionId { get; set; }
|
||||
|
||||
public AceType Reaction { get; set; }
|
||||
|
||||
|
||||
public Ace(Guid actionId, AceType reaction)
|
||||
{
|
||||
ActionId = actionId;
|
||||
Reaction = reaction;
|
||||
}
|
||||
}
|
||||
}
|
@ -24,11 +24,10 @@
|
||||
*/
|
||||
|
||||
|
||||
namespace ASC.Common.Security.Authorizing
|
||||
{
|
||||
namespace ASC.Common.Security.Authorizing;
|
||||
|
||||
public enum AceType
|
||||
{
|
||||
Allow,
|
||||
Deny,
|
||||
}
|
||||
}
|
@ -23,28 +23,25 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Security.Authorizing
|
||||
{
|
||||
namespace ASC.Common.Security.Authorizing;
|
||||
|
||||
[Serializable]
|
||||
public class Action : IAction
|
||||
{
|
||||
public Guid ID { get; private set; }
|
||||
|
||||
public string Name { get; private set; }
|
||||
|
||||
public bool AdministratorAlwaysAllow { get; private set; }
|
||||
|
||||
public bool Conjunction { get; private set; }
|
||||
|
||||
|
||||
public Action(Guid id, string name)
|
||||
: this(id, name, true, true)
|
||||
{
|
||||
}
|
||||
: this(id, name, true, true) { }
|
||||
|
||||
public Action(Guid id, string name, bool administratorAlwaysAllow, bool conjunction)
|
||||
{
|
||||
if (id == Guid.Empty) throw new ArgumentNullException(nameof(id));
|
||||
if (id == Guid.Empty)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(id));
|
||||
}
|
||||
|
||||
ID = id;
|
||||
Name = name;
|
||||
@ -67,4 +64,3 @@ namespace ASC.Common.Security.Authorizing
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,8 +23,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Security.Authorizing
|
||||
{
|
||||
namespace ASC.Common.Security.Authorizing;
|
||||
|
||||
[Serializable]
|
||||
public sealed class Role : IRole
|
||||
{
|
||||
@ -34,32 +34,27 @@ namespace ASC.Common.Security.Authorizing
|
||||
public const string Administrators = "Administrators";
|
||||
public const string System = "System";
|
||||
|
||||
|
||||
public Guid ID { get; internal set; }
|
||||
|
||||
public string Name { get; internal set; }
|
||||
|
||||
public string AuthenticationType
|
||||
{
|
||||
get { return "ASC"; }
|
||||
}
|
||||
|
||||
public bool IsAuthenticated
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
public string AuthenticationType => "ASC";
|
||||
public bool IsAuthenticated => false;
|
||||
|
||||
|
||||
public Role(Guid id, string name)
|
||||
{
|
||||
if (id == Guid.Empty) throw new ArgumentException("id");
|
||||
if (string.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name));
|
||||
if (id == Guid.Empty)
|
||||
{
|
||||
throw new ArgumentException(nameof(id));
|
||||
}
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(name));
|
||||
}
|
||||
|
||||
ID = id;
|
||||
Name = name;
|
||||
}
|
||||
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return ID.GetHashCode();
|
||||
@ -75,4 +70,3 @@ namespace ASC.Common.Security.Authorizing
|
||||
return $"Role: {Name}";
|
||||
}
|
||||
}
|
||||
}
|
@ -23,16 +23,12 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Security.Authorizing
|
||||
{
|
||||
namespace ASC.Common.Security.Authorizing;
|
||||
|
||||
public interface IAction
|
||||
{
|
||||
Guid ID { get; }
|
||||
|
||||
string Name { get; }
|
||||
|
||||
bool AdministratorAlwaysAllow { get; }
|
||||
|
||||
bool Conjunction { get; }
|
||||
}
|
||||
}
|
@ -24,9 +24,6 @@
|
||||
*/
|
||||
|
||||
|
||||
namespace ASC.Common.Security.Authorizing
|
||||
{
|
||||
public interface IRole : ISubject
|
||||
{
|
||||
}
|
||||
}
|
||||
namespace ASC.Common.Security.Authorizing;
|
||||
|
||||
public interface IRole : ISubject { }
|
||||
|
@ -23,10 +23,9 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Security.Authorizing
|
||||
{
|
||||
namespace ASC.Common.Security.Authorizing;
|
||||
|
||||
public interface ISubject : IIdentity
|
||||
{
|
||||
Guid ID { get; }
|
||||
}
|
||||
}
|
@ -24,16 +24,12 @@
|
||||
*/
|
||||
|
||||
|
||||
namespace ASC.Security.Cryptography
|
||||
{
|
||||
namespace ASC.Security.Cryptography;
|
||||
|
||||
public enum HashAlg
|
||||
{
|
||||
MD5,
|
||||
|
||||
SHA1,
|
||||
|
||||
SHA256,
|
||||
|
||||
SHA512
|
||||
}
|
||||
}
|
@ -23,13 +23,12 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Security.Cryptography
|
||||
{
|
||||
namespace ASC.Security.Cryptography;
|
||||
|
||||
public static class Hasher
|
||||
{
|
||||
private const HashAlg DefaultAlg = HashAlg.SHA256;
|
||||
|
||||
|
||||
public static byte[] Hash(string data, HashAlg hashAlg)
|
||||
{
|
||||
return ComputeHash(data, hashAlg);
|
||||
@ -70,14 +69,6 @@ namespace ASC.Security.Cryptography
|
||||
return Base64Hash(data, DefaultAlg);
|
||||
}
|
||||
|
||||
public static bool EqualHash(byte[] dataToCompare, byte[] hash, HashAlg hashAlg)
|
||||
{
|
||||
return string.Equals(
|
||||
ComputeHash64(dataToCompare, hashAlg),
|
||||
B2S64(hash)
|
||||
);
|
||||
}
|
||||
|
||||
public static bool EqualHash(byte[] dataToCompare, byte[] hash)
|
||||
{
|
||||
return EqualHash(dataToCompare, hash, DefaultAlg);
|
||||
@ -93,6 +84,13 @@ namespace ASC.Security.Cryptography
|
||||
return EqualHash(dataToCompare, hash, DefaultAlg);
|
||||
}
|
||||
|
||||
public static bool EqualHash(byte[] dataToCompare, byte[] hash, HashAlg hashAlg)
|
||||
{
|
||||
return string.Equals(
|
||||
ComputeHash64(dataToCompare, hashAlg),
|
||||
B2S64(hash)
|
||||
);
|
||||
}
|
||||
|
||||
private static HashAlgorithm GetAlg(HashAlg hashAlg)
|
||||
{
|
||||
@ -108,31 +106,48 @@ namespace ASC.Security.Cryptography
|
||||
|
||||
private static byte[] S2B(string str)
|
||||
{
|
||||
if (str == null) throw new ArgumentNullException(nameof(str));
|
||||
if (str == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(str));
|
||||
}
|
||||
|
||||
return Encoding.UTF8.GetBytes(str);
|
||||
}
|
||||
|
||||
private static string B2S(byte[] data)
|
||||
{
|
||||
if (data == null) throw new ArgumentNullException(nameof(data));
|
||||
if (data == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(data));
|
||||
}
|
||||
|
||||
return Encoding.UTF8.GetString(data);
|
||||
}
|
||||
|
||||
private static byte[] S642B(string str)
|
||||
{
|
||||
if (str == null) throw new ArgumentNullException(nameof(str));
|
||||
if (str == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(str));
|
||||
}
|
||||
|
||||
return Convert.FromBase64String(str);
|
||||
}
|
||||
|
||||
private static string B2S64(byte[] data)
|
||||
{
|
||||
if (data == null) throw new ArgumentNullException(nameof(data));
|
||||
if (data == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(data));
|
||||
}
|
||||
|
||||
return Convert.ToBase64String(data);
|
||||
}
|
||||
|
||||
private static byte[] ComputeHash(byte[] data, HashAlg hashAlg)
|
||||
{
|
||||
using var alg = GetAlg(hashAlg);
|
||||
|
||||
return alg.ComputeHash(data);
|
||||
}
|
||||
|
||||
@ -151,4 +166,3 @@ namespace ASC.Security.Cryptography
|
||||
return ComputeHash64(S2B(data), hashAlg);
|
||||
}
|
||||
}
|
||||
}
|
@ -23,16 +23,16 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Security.Cryptography
|
||||
{
|
||||
namespace ASC.Security.Cryptography;
|
||||
|
||||
[Singletone]
|
||||
public class InstanceCrypto
|
||||
{
|
||||
private byte[] EKey { get; }
|
||||
private readonly byte[] _eKey;
|
||||
|
||||
public InstanceCrypto(MachinePseudoKeys machinePseudoKeys)
|
||||
{
|
||||
EKey = machinePseudoKeys.GetMachineConstant(32);
|
||||
_eKey = machinePseudoKeys.GetMachineConstant(32);
|
||||
}
|
||||
|
||||
public string Encrypt(string data)
|
||||
@ -43,27 +43,26 @@ namespace ASC.Security.Cryptography
|
||||
public byte[] Encrypt(byte[] data)
|
||||
{
|
||||
using var hasher = Aes.Create();
|
||||
hasher.Key = EKey;
|
||||
hasher.Key = _eKey;
|
||||
hasher.IV = new byte[hasher.BlockSize >> 3];
|
||||
|
||||
using var ms = new MemoryStream();
|
||||
using var ss = new CryptoStream(ms, hasher.CreateEncryptor(), CryptoStreamMode.Write);
|
||||
using var plainTextStream = new MemoryStream(data);
|
||||
|
||||
plainTextStream.CopyTo(ss);
|
||||
ss.FlushFinalBlock();
|
||||
hasher.Clear();
|
||||
|
||||
return ms.ToArray();
|
||||
}
|
||||
|
||||
public string Decrypt(string data)
|
||||
{
|
||||
return Decrypt(Convert.FromBase64String(data));
|
||||
}
|
||||
public string Decrypt(string data) => Decrypt(Convert.FromBase64String(data));
|
||||
|
||||
public string Decrypt(byte[] data)
|
||||
{
|
||||
using var hasher = Aes.Create();
|
||||
hasher.Key = EKey;
|
||||
hasher.Key = _eKey;
|
||||
hasher.IV = new byte[hasher.BlockSize >> 3];
|
||||
|
||||
using var msDecrypt = new MemoryStream(data);
|
||||
@ -75,4 +74,3 @@ namespace ASC.Security.Cryptography
|
||||
return srDecrypt.ReadToEnd();
|
||||
}
|
||||
}
|
||||
}
|
@ -23,36 +23,39 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Security.Cryptography
|
||||
{
|
||||
namespace ASC.Security.Cryptography;
|
||||
|
||||
[Singletone]
|
||||
public class MachinePseudoKeys
|
||||
{
|
||||
private readonly byte[] confkey = null;
|
||||
private readonly byte[] _confKey = null;
|
||||
|
||||
public MachinePseudoKeys(IConfiguration configuration)
|
||||
{
|
||||
var key = configuration["core:machinekey"];
|
||||
|
||||
if (string.IsNullOrEmpty(key))
|
||||
{
|
||||
key = configuration["asc:common.machinekey"];
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(key))
|
||||
{
|
||||
confkey = Encoding.UTF8.GetBytes(key);
|
||||
_confKey = Encoding.UTF8.GetBytes(key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public byte[] GetMachineConstant()
|
||||
{
|
||||
if (confkey != null)
|
||||
if (_confKey != null)
|
||||
{
|
||||
return confkey;
|
||||
return _confKey;
|
||||
}
|
||||
|
||||
var path = typeof(MachinePseudoKeys).Assembly.Location;
|
||||
var fi = new FileInfo(path);
|
||||
|
||||
return BitConverter.GetBytes(fi.CreationTime.ToOADate());
|
||||
}
|
||||
|
||||
@ -63,7 +66,7 @@ namespace ASC.Security.Cryptography
|
||||
var rnd = new AscRandom(icnst);
|
||||
var buff = new byte[bytesCount];
|
||||
rnd.NextBytes(buff);
|
||||
|
||||
return buff;
|
||||
}
|
||||
}
|
||||
}
|
@ -23,17 +23,29 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Security.Cryptography
|
||||
{
|
||||
namespace ASC.Security.Cryptography;
|
||||
|
||||
[Singletone]
|
||||
public class PasswordHasher
|
||||
{
|
||||
public int Size { get; private set; }
|
||||
public int Iterations { get; private set; }
|
||||
public string Salt { get; private set; }
|
||||
|
||||
public PasswordHasher(IConfiguration configuration, MachinePseudoKeys machinePseudoKeys)
|
||||
{
|
||||
if (!int.TryParse(configuration["core:password:size"], out var size)) size = 256;
|
||||
if (!int.TryParse(configuration["core:password:size"], out var size))
|
||||
{
|
||||
size = 256;
|
||||
}
|
||||
|
||||
Size = size;
|
||||
|
||||
if (!int.TryParse(configuration["core.password.iterations"], out var iterations)) iterations = 100000;
|
||||
if (!int.TryParse(configuration["core.password.iterations"], out var iterations))
|
||||
{
|
||||
iterations = 100000;
|
||||
}
|
||||
|
||||
Iterations = iterations;
|
||||
|
||||
Salt = (configuration["core:password:salt"] ?? "").Trim();
|
||||
@ -51,27 +63,12 @@ namespace ASC.Security.Cryptography
|
||||
}
|
||||
}
|
||||
|
||||
public int Size
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public int Iterations
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public string Salt
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public string GetClientPassword(string password)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(password)) password = Guid.NewGuid().ToString();
|
||||
if (string.IsNullOrWhiteSpace(password))
|
||||
{
|
||||
password = Guid.NewGuid().ToString();
|
||||
}
|
||||
|
||||
var salt = new UTF8Encoding(false).GetBytes(Salt);
|
||||
|
||||
@ -87,5 +84,3 @@ namespace ASC.Security.Cryptography
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -24,9 +24,6 @@
|
||||
*/
|
||||
|
||||
|
||||
namespace ASC.Common.Security
|
||||
{
|
||||
public interface ISecurityObject : ISecurityObjectId, ISecurityObjectProvider
|
||||
{
|
||||
}
|
||||
}
|
||||
namespace ASC.Common.Security;
|
||||
|
||||
public interface ISecurityObject : ISecurityObjectId, ISecurityObjectProvider { }
|
||||
|
@ -24,11 +24,10 @@
|
||||
*/
|
||||
|
||||
|
||||
namespace ASC.Common.Security
|
||||
{
|
||||
namespace ASC.Common.Security;
|
||||
|
||||
public interface ISecurityObjectId
|
||||
{
|
||||
object SecurityId { get; }
|
||||
Type ObjectType { get; }
|
||||
}
|
||||
}
|
@ -24,15 +24,13 @@
|
||||
*/
|
||||
|
||||
|
||||
namespace ASC.Common.Security
|
||||
{
|
||||
namespace ASC.Common.Security;
|
||||
|
||||
public interface ISecurityObjectProvider
|
||||
{
|
||||
bool InheritSupported { get; }
|
||||
|
||||
bool ObjectRolesSupported { get; }
|
||||
ISecurityObjectId InheritFrom(ISecurityObjectId objectId);
|
||||
|
||||
ISecurityObjectId InheritFrom(ISecurityObjectId objectId);
|
||||
IEnumerable<IRole> GetObjectRoles(ISubject account, ISecurityObjectId objectId, SecurityCallContext callContext);
|
||||
}
|
||||
}
|
@ -23,20 +23,17 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Security
|
||||
{
|
||||
namespace ASC.Common.Security;
|
||||
|
||||
public class SecurityCallContext
|
||||
{
|
||||
public object UserData { get; set; }
|
||||
public List<ISecurityObjectId> ObjectsStack { get; private set; }
|
||||
public List<IRole> RolesList { get; private set; }
|
||||
|
||||
public SecurityCallContext()
|
||||
{
|
||||
ObjectsStack = new List<ISecurityObjectId>();
|
||||
RolesList = new List<IRole>();
|
||||
}
|
||||
|
||||
public List<ISecurityObjectId> ObjectsStack { get; private set; }
|
||||
|
||||
public List<IRole> RolesList { get; private set; }
|
||||
|
||||
public object UserData { get; set; }
|
||||
}
|
||||
}
|
@ -23,16 +23,14 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Security
|
||||
{
|
||||
namespace ASC.Common.Security;
|
||||
|
||||
[DebuggerDisplay("ObjectType: {ObjectType.Name}, SecurityId: {SecurityId}")]
|
||||
public class SecurityObjectId : ISecurityObjectId
|
||||
{
|
||||
public object SecurityId { get; private set; }
|
||||
|
||||
public Type ObjectType { get; private set; }
|
||||
|
||||
|
||||
public SecurityObjectId(object id, Type objType)
|
||||
{
|
||||
SecurityId = id;
|
||||
@ -50,4 +48,3 @@ namespace ASC.Common.Security
|
||||
Equals(AzObjectIdHelper.GetFullObjectId(other), AzObjectIdHelper.GetFullObjectId(this));
|
||||
}
|
||||
}
|
||||
}
|
@ -25,60 +25,32 @@
|
||||
|
||||
using JsonSerializer = System.Text.Json.JsonSerializer;
|
||||
|
||||
namespace ASC.Common.Threading
|
||||
{
|
||||
namespace ASC.Common.Threading;
|
||||
|
||||
public class DistributedTask
|
||||
{
|
||||
public Action<DistributedTask> Publication { get; set; }
|
||||
|
||||
protected internal DistributedTaskCache DistributedTaskCache { get; internal set; }
|
||||
|
||||
public int InstanceId
|
||||
{
|
||||
get
|
||||
{
|
||||
return DistributedTaskCache.InstanceId;
|
||||
}
|
||||
set
|
||||
{
|
||||
DistributedTaskCache.InstanceId = value;
|
||||
}
|
||||
get => DistributedTaskCache.InstanceId;
|
||||
set => DistributedTaskCache.InstanceId = value;
|
||||
}
|
||||
public string Id
|
||||
{
|
||||
get
|
||||
{
|
||||
return DistributedTaskCache.Id;
|
||||
get => DistributedTaskCache.Id;
|
||||
protected set => DistributedTaskCache.Id = value ?? "";
|
||||
}
|
||||
protected set
|
||||
{
|
||||
DistributedTaskCache.Id = value ?? "";
|
||||
}
|
||||
}
|
||||
|
||||
public DistributedTaskStatus Status
|
||||
{
|
||||
get
|
||||
{
|
||||
return Enum.Parse<DistributedTaskStatus>(DistributedTaskCache.Status);
|
||||
get => Enum.Parse<DistributedTaskStatus>(DistributedTaskCache.Status);
|
||||
set => DistributedTaskCache.Status = value.ToString();
|
||||
}
|
||||
set
|
||||
{
|
||||
DistributedTaskCache.Status = value.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public Exception Exception
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Exception(DistributedTaskCache.Exception);
|
||||
}
|
||||
set
|
||||
{
|
||||
DistributedTaskCache.Exception = value?.ToString() ?? "";
|
||||
}
|
||||
get => new Exception(DistributedTaskCache.Exception);
|
||||
set => DistributedTaskCache.Exception = value?.ToString() ?? "";
|
||||
}
|
||||
protected internal DistributedTaskCache DistributedTaskCache { get; internal set; }
|
||||
|
||||
public DistributedTask()
|
||||
{
|
||||
@ -87,17 +59,19 @@ namespace ASC.Common.Threading
|
||||
Id = Guid.NewGuid().ToString()
|
||||
};
|
||||
}
|
||||
|
||||
public DistributedTask(DistributedTaskCache distributedTaskCache)
|
||||
{
|
||||
DistributedTaskCache = distributedTaskCache;
|
||||
}
|
||||
|
||||
|
||||
public T GetProperty<T>(string name)
|
||||
{
|
||||
var prop = DistributedTaskCache.Props.FirstOrDefault(r => r.Key == name);
|
||||
|
||||
if (prop == null) return default;
|
||||
if (prop == null)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
return JsonSerializer.Deserialize<T>(prop.Value);
|
||||
}
|
||||
@ -128,7 +102,7 @@ namespace ASC.Common.Threading
|
||||
{
|
||||
throw new InvalidOperationException("Publication not found.");
|
||||
}
|
||||
|
||||
Publication(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,44 +1,34 @@
|
||||
namespace ASC.Common.Threading
|
||||
{
|
||||
namespace ASC.Common.Threading;
|
||||
|
||||
[Transient]
|
||||
public class DistributedTaskProgress : DistributedTask
|
||||
{
|
||||
public double Percentage
|
||||
{
|
||||
get
|
||||
{
|
||||
return Math.Min(100.0, Math.Max(0, DistributedTaskCache.Percentage));
|
||||
get => Math.Min(100.0, Math.Max(0, DistributedTaskCache.Percentage));
|
||||
set => DistributedTaskCache.Percentage = value;
|
||||
}
|
||||
set
|
||||
{
|
||||
DistributedTaskCache.Percentage = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsCompleted
|
||||
{
|
||||
get
|
||||
{
|
||||
return DistributedTaskCache.IsCompleted;
|
||||
get => DistributedTaskCache.IsCompleted;
|
||||
set => DistributedTaskCache.IsCompleted = value;
|
||||
}
|
||||
set
|
||||
{
|
||||
DistributedTaskCache.IsCompleted = value;
|
||||
}
|
||||
}
|
||||
|
||||
protected int StepCount
|
||||
{
|
||||
get
|
||||
get => DistributedTaskCache.StepCount;
|
||||
set => DistributedTaskCache.StepCount = value;
|
||||
}
|
||||
|
||||
public void RunJob()
|
||||
{
|
||||
return DistributedTaskCache.StepCount;
|
||||
}
|
||||
set
|
||||
{
|
||||
DistributedTaskCache.StepCount = value;
|
||||
}
|
||||
Percentage = 0;
|
||||
Status = DistributedTaskStatus.Running;
|
||||
|
||||
DoJob();
|
||||
}
|
||||
|
||||
protected virtual void DoJob() { }
|
||||
|
||||
protected void StepDone()
|
||||
{
|
||||
if (StepCount > 0)
|
||||
@ -48,17 +38,4 @@
|
||||
|
||||
PublishChanges();
|
||||
}
|
||||
|
||||
public void RunJob()
|
||||
{
|
||||
Percentage = 0;
|
||||
Status = DistributedTaskStatus.Running;
|
||||
DoJob();
|
||||
}
|
||||
|
||||
protected virtual void DoJob()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -23,28 +23,29 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Threading
|
||||
{
|
||||
namespace ASC.Common.Threading;
|
||||
|
||||
[Singletone]
|
||||
public class DistributedTaskCacheNotify
|
||||
{
|
||||
public ConcurrentDictionary<string, CancellationTokenSource> Cancelations { get; }
|
||||
public ICache Cache { get; }
|
||||
private readonly ICacheNotify<DistributedTaskCancelation> notify;
|
||||
private readonly ICacheNotify<DistributedTaskCache> notifyCache;
|
||||
|
||||
private readonly ICacheNotify<DistributedTaskCancelation> _cancelTaskNotify;
|
||||
private readonly ICacheNotify<DistributedTaskCache> _taskCacheNotify;
|
||||
|
||||
public DistributedTaskCacheNotify(
|
||||
ICacheNotify<DistributedTaskCancelation> notify,
|
||||
ICacheNotify<DistributedTaskCache> notifyCache,
|
||||
ICacheNotify<DistributedTaskCancelation> cancelTaskNotify,
|
||||
ICacheNotify<DistributedTaskCache> taskCacheNotify,
|
||||
ICache cache)
|
||||
{
|
||||
Cancelations = new ConcurrentDictionary<string, CancellationTokenSource>();
|
||||
|
||||
Cache = cache;
|
||||
|
||||
this.notify = notify;
|
||||
_cancelTaskNotify = cancelTaskNotify;
|
||||
|
||||
notify.Subscribe((c) =>
|
||||
cancelTaskNotify.Subscribe((c) =>
|
||||
{
|
||||
if (Cancelations.TryGetValue(c.Id, out var s))
|
||||
{
|
||||
@ -52,14 +53,14 @@ namespace ASC.Common.Threading
|
||||
}
|
||||
}, CacheNotifyAction.Remove);
|
||||
|
||||
this.notifyCache = notifyCache;
|
||||
_taskCacheNotify = taskCacheNotify;
|
||||
|
||||
notifyCache.Subscribe((c) =>
|
||||
taskCacheNotify.Subscribe((c) =>
|
||||
{
|
||||
Cache.HashSet(c.Key, c.Id, (DistributedTaskCache)null);
|
||||
}, CacheNotifyAction.Remove);
|
||||
|
||||
notifyCache.Subscribe((c) =>
|
||||
taskCacheNotify.Subscribe((c) =>
|
||||
{
|
||||
Cache.HashSet(c.Key, c.Id, c);
|
||||
}, CacheNotifyAction.InsertOrUpdate);
|
||||
@ -67,26 +68,24 @@ namespace ASC.Common.Threading
|
||||
|
||||
public void CancelTask(string id)
|
||||
{
|
||||
notify.Publish(new DistributedTaskCancelation() { Id = id }, CacheNotifyAction.Remove);
|
||||
_cancelTaskNotify.Publish(new DistributedTaskCancelation() { Id = id }, CacheNotifyAction.Remove);
|
||||
}
|
||||
|
||||
public void SetTask(DistributedTask task)
|
||||
{
|
||||
notifyCache.Publish(task.DistributedTaskCache, CacheNotifyAction.InsertOrUpdate);
|
||||
_taskCacheNotify.Publish(task.DistributedTaskCache, CacheNotifyAction.InsertOrUpdate);
|
||||
}
|
||||
|
||||
public void RemoveTask(string id, string key)
|
||||
{
|
||||
notifyCache.Publish(new DistributedTaskCache() { Id = id, Key = key }, CacheNotifyAction.Remove);
|
||||
_taskCacheNotify.Publish(new DistributedTaskCache() { Id = id, Key = key }, CacheNotifyAction.Remove);
|
||||
}
|
||||
}
|
||||
|
||||
[Singletone(typeof(ConfigureDistributedTaskQueue))]
|
||||
public class DistributedTaskQueueOptionsManager : OptionsManager<DistributedTaskQueue>
|
||||
{
|
||||
public DistributedTaskQueueOptionsManager(IOptionsFactory<DistributedTaskQueue> factory) : base(factory)
|
||||
{
|
||||
}
|
||||
public DistributedTaskQueueOptionsManager(IOptionsFactory<DistributedTaskQueue> factory) : base(factory) { }
|
||||
|
||||
public DistributedTaskQueue Get<T>() where T : DistributedTask
|
||||
{
|
||||
@ -97,19 +96,21 @@ namespace ASC.Common.Threading
|
||||
[Scope]
|
||||
public class ConfigureDistributedTaskQueue : IConfigureNamedOptions<DistributedTaskQueue>
|
||||
{
|
||||
private DistributedTaskCacheNotify DistributedTaskCacheNotify { get; }
|
||||
public IServiceProvider ServiceProvider { get; }
|
||||
private readonly DistributedTaskCacheNotify _distributedTaskCacheNotify;
|
||||
public readonly IServiceProvider _serviceProvider;
|
||||
|
||||
public ConfigureDistributedTaskQueue(DistributedTaskCacheNotify distributedTaskCacheNotify, IServiceProvider serviceProvider)
|
||||
public ConfigureDistributedTaskQueue(
|
||||
DistributedTaskCacheNotify distributedTaskCacheNotify,
|
||||
IServiceProvider serviceProvider)
|
||||
{
|
||||
DistributedTaskCacheNotify = distributedTaskCacheNotify;
|
||||
ServiceProvider = serviceProvider;
|
||||
_distributedTaskCacheNotify = distributedTaskCacheNotify;
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public void Configure(DistributedTaskQueue queue)
|
||||
{
|
||||
queue.DistributedTaskCacheNotify = DistributedTaskCacheNotify;
|
||||
queue.ServiceProvider = ServiceProvider;
|
||||
queue.DistributedTaskCacheNotify = _distributedTaskCacheNotify;
|
||||
queue.ServiceProvider = _serviceProvider;
|
||||
}
|
||||
|
||||
public void Configure(string name, DistributedTaskQueue options)
|
||||
@ -121,27 +122,33 @@ namespace ASC.Common.Threading
|
||||
|
||||
public class DistributedTaskQueue
|
||||
{
|
||||
public static readonly int InstanceId = Process.GetCurrentProcess().Id;
|
||||
|
||||
private string key;
|
||||
private TaskScheduler Scheduler { get; set; } = TaskScheduler.Default;
|
||||
|
||||
public IServiceProvider ServiceProvider { get; set; }
|
||||
public string Name { get { return key; } set { key = value + GetType().Name; } }
|
||||
private ICache Cache { get => DistributedTaskCacheNotify.Cache; }
|
||||
private ConcurrentDictionary<string, CancellationTokenSource> Cancelations { get => DistributedTaskCacheNotify.Cancelations; }
|
||||
|
||||
public DistributedTaskCacheNotify DistributedTaskCacheNotify { get; set; }
|
||||
public string Name
|
||||
{
|
||||
get => _name;
|
||||
set => _name = value + GetType().Name;
|
||||
}
|
||||
public int MaxThreadsCount
|
||||
{
|
||||
set
|
||||
{
|
||||
Scheduler = value <= 0
|
||||
set => Scheduler = value <= 0
|
||||
? TaskScheduler.Default
|
||||
: new ConcurrentExclusiveSchedulerPair(TaskScheduler.Default, value).ConcurrentScheduler;
|
||||
}
|
||||
}
|
||||
|
||||
public DistributedTaskCacheNotify DistributedTaskCacheNotify { get; set; }
|
||||
private ICache Cache => DistributedTaskCacheNotify.Cache;
|
||||
private TaskScheduler Scheduler { get; set; } = TaskScheduler.Default;
|
||||
|
||||
public static readonly int InstanceId = Process.GetCurrentProcess().Id;
|
||||
private string _name;
|
||||
|
||||
private ConcurrentDictionary<string, CancellationTokenSource> Cancelations
|
||||
{
|
||||
get
|
||||
{
|
||||
return DistributedTaskCacheNotify.Cancelations;
|
||||
}
|
||||
}
|
||||
|
||||
public void QueueTask(DistributedTaskProgress taskProgress)
|
||||
{
|
||||
@ -162,8 +169,8 @@ namespace ASC.Common.Threading
|
||||
Cancelations[distributedTask.Id] = cancelation;
|
||||
|
||||
var task = new Task(() => { action(distributedTask, token); }, token, TaskCreationOptions.LongRunning);
|
||||
task
|
||||
.ConfigureAwait(false)
|
||||
|
||||
task.ConfigureAwait(false)
|
||||
.GetAwaiter()
|
||||
.OnCompleted(() => OnCompleted(task, distributedTask.Id));
|
||||
|
||||
@ -173,6 +180,7 @@ namespace ASC.Common.Threading
|
||||
{
|
||||
distributedTask.Publication = GetPublication();
|
||||
}
|
||||
|
||||
distributedTask.PublishChanges();
|
||||
|
||||
task.Start(Scheduler);
|
||||
@ -180,7 +188,8 @@ namespace ASC.Common.Threading
|
||||
|
||||
public IEnumerable<DistributedTask> GetTasks()
|
||||
{
|
||||
var tasks = Cache.HashGetAll<DistributedTaskCache>(key).Values.Select(r => new DistributedTask(r)).ToList();
|
||||
var tasks = Cache.HashGetAll<DistributedTaskCache>(_name).Values.Select(r => new DistributedTask(r)).ToList();
|
||||
|
||||
tasks.ForEach(t =>
|
||||
{
|
||||
if (t.Publication == null)
|
||||
@ -188,15 +197,17 @@ namespace ASC.Common.Threading
|
||||
t.Publication = GetPublication();
|
||||
}
|
||||
});
|
||||
|
||||
return tasks;
|
||||
}
|
||||
|
||||
public IEnumerable<T> GetTasks<T>() where T : DistributedTask
|
||||
{
|
||||
var tasks = Cache.HashGetAll<DistributedTaskCache>(key).Values.Select(r =>
|
||||
var tasks = Cache.HashGetAll<DistributedTaskCache>(_name).Values.Select(r =>
|
||||
{
|
||||
var result = ServiceProvider.GetService<T>();
|
||||
result.DistributedTaskCache = r;
|
||||
|
||||
return result;
|
||||
}).ToList();
|
||||
|
||||
@ -207,12 +218,13 @@ namespace ASC.Common.Threading
|
||||
t.Publication = GetPublication();
|
||||
}
|
||||
});
|
||||
|
||||
return tasks;
|
||||
}
|
||||
|
||||
public T GetTask<T>(string id) where T : DistributedTask
|
||||
{
|
||||
var cache = Cache.HashGet<DistributedTaskCache>(key, id);
|
||||
var cache = Cache.HashGet<DistributedTaskCache>(_name, id);
|
||||
if (cache != null)
|
||||
{
|
||||
using var scope = ServiceProvider.CreateScope();
|
||||
@ -222,6 +234,7 @@ namespace ASC.Common.Threading
|
||||
{
|
||||
task.Publication = GetPublication();
|
||||
}
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
@ -230,7 +243,7 @@ namespace ASC.Common.Threading
|
||||
|
||||
public DistributedTask GetTask(string id)
|
||||
{
|
||||
var cache = Cache.HashGet<DistributedTaskCache>(key, id);
|
||||
var cache = Cache.HashGet<DistributedTaskCache>(_name, id);
|
||||
if (cache != null)
|
||||
{
|
||||
var task = new DistributedTask();
|
||||
@ -239,6 +252,7 @@ namespace ASC.Common.Threading
|
||||
{
|
||||
task.Publication = GetPublication();
|
||||
}
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
@ -252,7 +266,7 @@ namespace ASC.Common.Threading
|
||||
|
||||
public void RemoveTask(string id)
|
||||
{
|
||||
DistributedTaskCacheNotify.RemoveTask(id, key);
|
||||
DistributedTaskCacheNotify.RemoveTask(id, _name);
|
||||
}
|
||||
|
||||
public void CancelTask(string id)
|
||||
@ -267,10 +281,12 @@ namespace ASC.Common.Threading
|
||||
{
|
||||
distributedTask.Status = DistributedTaskStatus.Completed;
|
||||
distributedTask.Exception = task.Exception;
|
||||
|
||||
if (task.IsFaulted)
|
||||
{
|
||||
distributedTask.Status = DistributedTaskStatus.Failted;
|
||||
}
|
||||
|
||||
if (task.IsCanceled)
|
||||
{
|
||||
distributedTask.Status = DistributedTaskStatus.Canceled;
|
||||
@ -288,8 +304,9 @@ namespace ASC.Common.Threading
|
||||
{
|
||||
if (t.DistributedTaskCache != null)
|
||||
{
|
||||
t.DistributedTaskCache.Key = key;
|
||||
t.DistributedTaskCache.Key = _name;
|
||||
}
|
||||
|
||||
DistributedTaskCacheNotify.SetTask(t);
|
||||
};
|
||||
}
|
||||
@ -304,6 +321,7 @@ namespace ASC.Common.Threading
|
||||
services.TryAdd<DistributedTaskQueue>();
|
||||
|
||||
var type = typeof(T);
|
||||
|
||||
if (!type.IsAbstract)
|
||||
{
|
||||
services.TryAdd<T>();
|
||||
@ -320,4 +338,3 @@ namespace ASC.Common.Threading
|
||||
return services;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,8 +24,8 @@
|
||||
*/
|
||||
|
||||
|
||||
namespace ASC.Common.Threading
|
||||
{
|
||||
namespace ASC.Common.Threading;
|
||||
|
||||
public enum DistributedTaskStatus
|
||||
{
|
||||
Created,
|
||||
@ -34,4 +34,3 @@ namespace ASC.Common.Threading
|
||||
Canceled,
|
||||
Failted
|
||||
}
|
||||
}
|
||||
|
@ -23,8 +23,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Threading.Progress
|
||||
{
|
||||
namespace ASC.Common.Threading.Progress;
|
||||
|
||||
public interface IProgressItem : ICloneable
|
||||
{
|
||||
string Id { get; }
|
||||
@ -35,4 +35,3 @@ namespace ASC.Common.Threading.Progress
|
||||
|
||||
void RunJob();
|
||||
}
|
||||
}
|
@ -1,47 +1,50 @@
|
||||
namespace ASC.Common.Utils
|
||||
{
|
||||
namespace ASC.Common.Utils;
|
||||
|
||||
public class ConnectionStringCollection : IEnumerable<ConnectionStringSettings>
|
||||
{
|
||||
private List<ConnectionStringSettings> Data { get; set; }
|
||||
private List<ConnectionStringSettings> _data;
|
||||
|
||||
public ConnectionStringCollection(IEnumerable<ConnectionStringSettings> data) => Data = data.ToList();
|
||||
public ConnectionStringSettings this[string name] => _data.FirstOrDefault(r => r.Name == name);
|
||||
|
||||
public ConnectionStringCollection(IEnumerable<ConnectionStringSettings> data)
|
||||
{
|
||||
_data = data.ToList();
|
||||
}
|
||||
|
||||
public IEnumerator<ConnectionStringSettings> GetEnumerator()
|
||||
{
|
||||
return Data.GetEnumerator();
|
||||
return _data.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
public ConnectionStringSettings this[string name]
|
||||
{
|
||||
get
|
||||
{
|
||||
return Data.FirstOrDefault(r => r.Name == name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Singletone]
|
||||
public class ConfigurationExtension
|
||||
{
|
||||
private IConfiguration Configuration { get; }
|
||||
private Lazy<ConnectionStringCollection> ConnectionStringSettings { get; }
|
||||
public string this[string key]
|
||||
{
|
||||
get => _configuration[key];
|
||||
set => _configuration[key] = value;
|
||||
}
|
||||
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly Lazy<ConnectionStringCollection> _connectionStringSettings;
|
||||
|
||||
public ConfigurationExtension(IConfiguration configuration)
|
||||
{
|
||||
Configuration = configuration;
|
||||
ConnectionStringSettings = new Lazy<ConnectionStringCollection>(new ConnectionStringCollection(GetSettings<ConnectionStringSettings>("ConnectionStrings")));
|
||||
_configuration = configuration;
|
||||
_connectionStringSettings = new Lazy<ConnectionStringCollection>(new ConnectionStringCollection(GetSettings<ConnectionStringSettings>("ConnectionStrings")));
|
||||
}
|
||||
|
||||
public IEnumerable<T> GetSettings<T>(string section) where T : new()
|
||||
{
|
||||
var result = new List<T>();
|
||||
|
||||
var sectionSettings = Configuration.GetSection(section);
|
||||
var sectionSettings = _configuration.GetSection(section);
|
||||
|
||||
foreach (var ch in sectionSettings.GetChildren())
|
||||
{
|
||||
@ -60,7 +63,7 @@
|
||||
|
||||
public T GetSetting<T>(string section, T instance)
|
||||
{
|
||||
var sectionSettings = Configuration.GetSection(section);
|
||||
var sectionSettings = _configuration.GetSection(section);
|
||||
|
||||
sectionSettings.Bind(instance);
|
||||
|
||||
@ -69,18 +72,11 @@
|
||||
|
||||
public ConnectionStringCollection GetConnectionStrings()
|
||||
{
|
||||
return ConnectionStringSettings.Value;
|
||||
return _connectionStringSettings.Value;
|
||||
}
|
||||
|
||||
public ConnectionStringSettings GetConnectionStrings(string key)
|
||||
{
|
||||
return GetConnectionStrings()[key];
|
||||
}
|
||||
|
||||
public string this[string key]
|
||||
{
|
||||
get => Configuration[key];
|
||||
set => Configuration[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,17 +23,20 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Utils
|
||||
{
|
||||
namespace ASC.Common.Utils;
|
||||
|
||||
public static class CrossPlatform
|
||||
{
|
||||
private static char[] _pathSplitCharacters = new char[] { '/', '\\' };
|
||||
|
||||
public static string PathCombine(string basePath, params string[] additional)
|
||||
{
|
||||
var splits = additional.Select(s => s.Split(pathSplitCharacters)).ToArray();
|
||||
var splits = additional.Select(s => s.Split(_pathSplitCharacters)).ToArray();
|
||||
var totalLength = splits.Sum(arr => arr.Length);
|
||||
var segments = new string[totalLength + 1];
|
||||
segments[0] = basePath;
|
||||
var i = 0;
|
||||
|
||||
foreach (var split in splits)
|
||||
{
|
||||
foreach (var value in split)
|
||||
@ -42,9 +45,7 @@ namespace ASC.Common.Utils
|
||||
segments[i] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return Path.Combine(segments);
|
||||
}
|
||||
|
||||
static char[] pathSplitCharacters = new char[] { '/', '\\' };
|
||||
}
|
||||
}
|
||||
|
@ -23,8 +23,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Utils
|
||||
{
|
||||
namespace ASC.Common.Utils;
|
||||
|
||||
public class DnsLookup
|
||||
{
|
||||
private readonly IDnsResolver _sDnsResolver;
|
||||
@ -46,7 +46,9 @@ namespace ASC.Common.Utils
|
||||
public List<MxRecord> GetDomainMxRecords(string domainName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(domainName))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(domainName));
|
||||
}
|
||||
|
||||
var mxRecords = DnsResolve<MxRecord>(domainName, RecordType.Mx);
|
||||
|
||||
@ -64,10 +66,14 @@ namespace ASC.Common.Utils
|
||||
public bool IsDomainMxRecordExists(string domainName, string mxRecord)
|
||||
{
|
||||
if (string.IsNullOrEmpty(domainName))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(domainName));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(mxRecord))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(mxRecord));
|
||||
}
|
||||
|
||||
var mxDomain = DomainName.Parse(mxRecord);
|
||||
|
||||
@ -88,7 +94,9 @@ namespace ASC.Common.Utils
|
||||
public bool IsDomainExists(string domainName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(domainName))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(domainName));
|
||||
}
|
||||
|
||||
var dnsMessage = GetDnsMessage(domainName);
|
||||
|
||||
@ -105,7 +113,9 @@ namespace ASC.Common.Utils
|
||||
public List<ARecord> GetDomainARecords(string domainName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(domainName))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(domainName));
|
||||
}
|
||||
|
||||
var aRecords = DnsResolve<ARecord>(domainName, RecordType.A);
|
||||
|
||||
@ -122,7 +132,9 @@ namespace ASC.Common.Utils
|
||||
public List<IPAddress> GetDomainIPs(string domainName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(domainName))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(domainName));
|
||||
}
|
||||
|
||||
var addresses = _sDnsResolver.ResolveHost(domainName);
|
||||
|
||||
@ -139,7 +151,9 @@ namespace ASC.Common.Utils
|
||||
public List<TxtRecord> GetDomainTxtRecords(string domainName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(domainName))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(domainName));
|
||||
}
|
||||
|
||||
var txtRecords = DnsResolve<TxtRecord>(domainName, RecordType.Txt);
|
||||
|
||||
@ -193,10 +207,13 @@ namespace ASC.Common.Utils
|
||||
public bool IsDomainPtrRecordExists(IPAddress ipAddress, string domainName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(domainName))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(domainName));
|
||||
|
||||
}
|
||||
if (ipAddress == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(ipAddress));
|
||||
}
|
||||
|
||||
var domain = DomainName.Parse(domainName);
|
||||
|
||||
@ -222,12 +239,13 @@ namespace ASC.Common.Utils
|
||||
private DnsMessage GetDnsMessage(string domainName, RecordType? type = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(domainName))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(domainName));
|
||||
}
|
||||
|
||||
var domain = DomainName.Parse(domainName);
|
||||
|
||||
var dnsMessage = type.HasValue ? _dnsClient.Resolve(domain, type.Value) : _dnsClient.Resolve(domain);
|
||||
|
||||
if ((dnsMessage == null) ||
|
||||
((dnsMessage.ReturnCode != ReturnCode.NoError) && (dnsMessage.ReturnCode != ReturnCode.NxDomain)))
|
||||
{
|
||||
@ -240,11 +258,12 @@ namespace ASC.Common.Utils
|
||||
private List<T> DnsResolve<T>(string domainName, RecordType type)
|
||||
{
|
||||
if (string.IsNullOrEmpty(domainName))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(domainName));
|
||||
}
|
||||
|
||||
var dnsMessage = GetDnsMessage(domainName, type);
|
||||
|
||||
return dnsMessage.AnswerRecords.Where(r => r.RecordType == type).Cast<T>().ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,34 +23,42 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Utils
|
||||
{
|
||||
namespace ASC.Common.Utils;
|
||||
|
||||
public static class HtmlUtil
|
||||
{
|
||||
private static readonly Regex tagReplacer = new Regex("<[^>]*>", RegexOptions.Multiline | RegexOptions.Compiled);
|
||||
private static readonly Regex commentsReplacer = new Regex("<!--(?s).*?-->", RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
private static readonly Regex xssReplacer = new Regex(@"<\s*(style|script)[^>]*>(.*?)<\s*/\s*(style|script)>", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled | RegexOptions.Singleline);
|
||||
private static readonly Regex Worder = new Regex(@"\S+", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
|
||||
private static readonly Regex _tagReplacer
|
||||
= new Regex("<[^>]*>", RegexOptions.Multiline | RegexOptions.Compiled);
|
||||
|
||||
private static readonly Regex _commentsReplacer
|
||||
= new Regex("<!--(?s).*?-->", RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
private static readonly Regex _xssReplacer
|
||||
= new Regex(@"<\s*(style|script)[^>]*>(.*?)<\s*/\s*(style|script)>", RegexOptions.IgnoreCase
|
||||
| RegexOptions.CultureInvariant | RegexOptions.Compiled | RegexOptions.Singleline);
|
||||
|
||||
private static readonly Regex _worder =
|
||||
new Regex(@"\S+", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
|
||||
|
||||
public static string GetText(string html, int maxLength = 0, string endBlockTemplate = "...")
|
||||
{
|
||||
var unformatedText = string.Empty;
|
||||
|
||||
if (!string.IsNullOrEmpty(html))
|
||||
{
|
||||
html = xssReplacer.Replace(html, string.Empty); //Clean malicious tags. <script> <style>
|
||||
html = _xssReplacer.Replace(html, string.Empty); //Clean malicious tags. <script> <style>
|
||||
|
||||
if (string.IsNullOrEmpty(html))
|
||||
{
|
||||
return html;
|
||||
}
|
||||
|
||||
unformatedText = tagReplacer.Replace(html, string.Empty);
|
||||
unformatedText = _tagReplacer.Replace(html, string.Empty);
|
||||
|
||||
if (!string.IsNullOrEmpty(unformatedText))
|
||||
{
|
||||
// kill comments
|
||||
unformatedText = commentsReplacer.Replace(unformatedText, string.Empty);
|
||||
unformatedText = _commentsReplacer.Replace(unformatedText, string.Empty);
|
||||
unformatedText = unformatedText.Trim();
|
||||
|
||||
if (!string.IsNullOrEmpty(unformatedText))
|
||||
@ -70,6 +78,7 @@ namespace ASC.Common.Utils
|
||||
unformatedText = lastSpaceIndex > 0 && lastSpaceIndex < unformatedText.Length
|
||||
? unformatedText.Remove(lastSpaceIndex)
|
||||
: unformatedText.Substring(0, maxLength);
|
||||
|
||||
if (!string.IsNullOrEmpty(endBlockTemplate))
|
||||
{
|
||||
unformatedText += endBlockTemplate;
|
||||
@ -77,6 +86,7 @@ namespace ASC.Common.Utils
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return HttpUtility.HtmlDecode(unformatedText);//TODO:!!!
|
||||
}
|
||||
|
||||
@ -94,11 +104,13 @@ namespace ASC.Common.Utils
|
||||
/// <returns>highlighted html</returns>
|
||||
public static string SearchTextHighlight(string searchText, string htmlText, bool withoutLink = false)
|
||||
{
|
||||
if (string.IsNullOrEmpty(searchText) || string.IsNullOrEmpty(htmlText)) return htmlText;
|
||||
if (string.IsNullOrEmpty(searchText) || string.IsNullOrEmpty(htmlText))
|
||||
{
|
||||
return htmlText;
|
||||
}
|
||||
|
||||
var regexpstr = Worder.Matches(searchText).Select(m => m.Value).Distinct().Aggregate((r, n) => r + "|" + n);
|
||||
var regexpstr = _worder.Matches(searchText).Select(m => m.Value).Distinct().Aggregate((r, n) => r + "|" + n);
|
||||
var wordsFinder = new Regex(Regex.Escape(regexpstr), RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Multiline);
|
||||
return wordsFinder.Replace(htmlText, m => "<span class='searchTextHighlight" + (withoutLink ? " bold" : string.Empty) + "'>" + m.Value + "</span>");
|
||||
}
|
||||
}
|
||||
}
|
@ -26,17 +26,17 @@
|
||||
|
||||
using HttpContext = Microsoft.AspNetCore.Http.HttpContext;
|
||||
|
||||
namespace System.Web
|
||||
{
|
||||
namespace System.Web;
|
||||
|
||||
public static class HttpRequestExtensions
|
||||
{
|
||||
public static readonly string UrlRewriterHeader = "X-REWRITER-URL";
|
||||
|
||||
|
||||
public static Uri GetUrlRewriter(this HttpRequest request)
|
||||
{
|
||||
return request != null ? GetUrlRewriter(request.Headers, request) : null;
|
||||
}
|
||||
|
||||
public static Uri Url(this HttpRequest request)
|
||||
{
|
||||
return request != null ? new Uri(request.GetDisplayUrl()) : null;
|
||||
@ -53,6 +53,7 @@ namespace System.Web
|
||||
{
|
||||
var h = headers[UrlRewriterHeader];
|
||||
var rewriterUri = !string.IsNullOrEmpty(h) ? ParseRewriterUrl(h) : null;
|
||||
|
||||
if (request != null && rewriterUri != null)
|
||||
{
|
||||
var result = new UriBuilder()
|
||||
@ -61,8 +62,10 @@ namespace System.Web
|
||||
Host = rewriterUri.Host,
|
||||
Port = rewriterUri.Port
|
||||
};
|
||||
|
||||
result.Query = request.QueryString.Value;
|
||||
result.Path = request.Path.Value;
|
||||
|
||||
return result.Uri;
|
||||
}
|
||||
}
|
||||
@ -97,6 +100,7 @@ namespace System.Web
|
||||
public static Uri PushRewritenUri(this HttpContext context, Uri rewrittenUri)
|
||||
{
|
||||
Uri oldUri = null;
|
||||
|
||||
if (context != null)
|
||||
{
|
||||
var request = context.Request;
|
||||
@ -134,11 +138,10 @@ namespace System.Web
|
||||
context.Items["oldUri"] = oldUri;
|
||||
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
}
|
||||
|
||||
return oldUri;
|
||||
}
|
||||
|
||||
@ -147,11 +150,13 @@ namespace System.Web
|
||||
if (context != null && context.Items["oldUri"] != null)
|
||||
{
|
||||
var rewriteTo = context.Items["oldUri"] as Uri;
|
||||
|
||||
if (rewriteTo != null)
|
||||
{
|
||||
return PushRewritenUri(context, rewriteTo);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -169,6 +174,10 @@ namespace System.Web
|
||||
|| !string.IsNullOrEmpty(request.Headers[HeaderNames.UserAgent]) && request.Headers[HeaderNames.UserAgent].ToString().Contains("SailfishOS"));
|
||||
}
|
||||
|
||||
public static string GetUserHostAddress(this HttpRequest request)
|
||||
{
|
||||
return request.HttpContext.Features.Get<IHttpConnectionFeature>()?.RemoteIpAddress.ToString();
|
||||
}
|
||||
|
||||
private static Uri ParseRewriterUrl(string s)
|
||||
{
|
||||
@ -200,12 +209,7 @@ namespace System.Web
|
||||
}
|
||||
|
||||
Uri.TryCreate(s, UriKind.Absolute, out var result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static string GetUserHostAddress(this HttpRequest request)
|
||||
{
|
||||
return request.HttpContext.Features.Get<IHttpConnectionFeature>()?.RemoteIpAddress.ToString();
|
||||
}
|
||||
}
|
||||
}
|
@ -27,8 +27,8 @@
|
||||
using Formatting = Newtonsoft.Json.Formatting;
|
||||
using IJsonSerializer = JWT.IJsonSerializer;
|
||||
|
||||
namespace ASC.Web.Core.Files
|
||||
{
|
||||
namespace ASC.Web.Core.Files;
|
||||
|
||||
public static class JsonWebToken
|
||||
{
|
||||
public static string Encode(object payload, string key)
|
||||
@ -93,4 +93,3 @@ namespace ASC.Web.Core.Files
|
||||
return JsonConvert.DeserializeObject<T>(json, settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,8 +23,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Utils
|
||||
{
|
||||
namespace ASC.Common.Utils;
|
||||
|
||||
public static class MailAddressUtils
|
||||
{
|
||||
public static MailAddress Create(string address)
|
||||
@ -33,11 +33,13 @@ namespace ASC.Common.Utils
|
||||
{
|
||||
var firstPos = address.IndexOf('"');
|
||||
var lastPos = address.LastIndexOf('"');
|
||||
|
||||
if (firstPos != -1 && firstPos < lastPos && address.IndexOf('"', firstPos + 1, lastPos - firstPos - 1) != -1)
|
||||
{
|
||||
address = new StringBuilder(address).Replace("\"", string.Empty, firstPos + 1, lastPos - firstPos - 1).ToString();
|
||||
}
|
||||
}
|
||||
|
||||
return new MailAddress(address);
|
||||
}
|
||||
|
||||
@ -46,11 +48,13 @@ namespace ASC.Common.Utils
|
||||
if (!string.IsNullOrEmpty(displayName))
|
||||
{
|
||||
displayName = displayName.Replace("\"", string.Empty);
|
||||
|
||||
if (125 < displayName.Length)
|
||||
{
|
||||
displayName = displayName.Substring(0, 125);
|
||||
}
|
||||
}
|
||||
|
||||
return Create(ToSmtpAddress(address, displayName));
|
||||
}
|
||||
|
||||
@ -59,10 +63,8 @@ namespace ASC.Common.Utils
|
||||
return ToSmtpAddress(m.Address, MimeHeaderUtils.EncodeMime(m.DisplayName));
|
||||
}
|
||||
|
||||
|
||||
private static string ToSmtpAddress(string address, string displayName)
|
||||
{
|
||||
return $"\"{displayName}\" <{address}>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,8 +23,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Utils
|
||||
{
|
||||
namespace ASC.Common.Utils;
|
||||
|
||||
public static class MimeHeaderUtils
|
||||
{
|
||||
public static string EncodeMime(string mimeHeaderValue)
|
||||
@ -43,6 +43,7 @@ namespace ASC.Common.Utils
|
||||
result.Append("=?" + charset.WebName + "?B?");
|
||||
var stored = 0;
|
||||
var base64 = Convert.ToBase64String(data);
|
||||
|
||||
for (var i = 0; i < base64.Length; i += 4)
|
||||
{
|
||||
// Encoding buffer full, create new encoded-word.
|
||||
@ -55,9 +56,12 @@ namespace ASC.Common.Utils
|
||||
result.Append(base64, i, 4);
|
||||
stored += 4;
|
||||
}
|
||||
|
||||
result.Append("?=");
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
return mimeHeaderValue;
|
||||
}
|
||||
|
||||
@ -66,4 +70,3 @@ namespace ASC.Common.Utils
|
||||
return !string.IsNullOrEmpty(text) && text.Any(c => c > 127);
|
||||
}
|
||||
}
|
||||
}
|
@ -23,19 +23,20 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Utils
|
||||
{
|
||||
namespace ASC.Common.Utils;
|
||||
|
||||
public static class RandomString
|
||||
{
|
||||
public static string Generate(int length)
|
||||
{
|
||||
const string valid = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
|
||||
var res = new StringBuilder();
|
||||
|
||||
while (0 < length--)
|
||||
{
|
||||
res.Append(valid[RandomNumberGenerator.GetInt32(valid.Length)]);
|
||||
}
|
||||
|
||||
return res.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,33 +23,34 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Utils
|
||||
{
|
||||
namespace ASC.Common.Utils;
|
||||
|
||||
[Singletone]
|
||||
public class Signature
|
||||
{
|
||||
private readonly MachinePseudoKeys _machinePseudoKeys;
|
||||
|
||||
public Signature(MachinePseudoKeys machinePseudoKeys)
|
||||
{
|
||||
MachinePseudoKeys = machinePseudoKeys;
|
||||
_machinePseudoKeys = machinePseudoKeys;
|
||||
}
|
||||
|
||||
private MachinePseudoKeys MachinePseudoKeys { get; }
|
||||
|
||||
public string Create<T>(T obj)
|
||||
{
|
||||
return Create(obj, Encoding.UTF8.GetString(MachinePseudoKeys.GetMachineConstant()));
|
||||
return Create(obj, Encoding.UTF8.GetString(_machinePseudoKeys.GetMachineConstant()));
|
||||
}
|
||||
|
||||
public static string Create<T>(T obj, string secret)
|
||||
{
|
||||
var str = JsonConvert.SerializeObject(obj);
|
||||
var payload = GetHashBase64(str + secret) + "?" + str;
|
||||
|
||||
return WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(payload));
|
||||
}
|
||||
|
||||
public T Read<T>(string signature)
|
||||
{
|
||||
return Read<T>(signature, Encoding.UTF8.GetString(MachinePseudoKeys.GetMachineConstant()));
|
||||
return Read<T>(signature, Encoding.UTF8.GetString(_machinePseudoKeys.GetMachineConstant()));
|
||||
}
|
||||
|
||||
public static T Read<T>(string signature, string secret)
|
||||
@ -58,28 +59,28 @@ namespace ASC.Common.Utils
|
||||
{
|
||||
var rightSignature = signature.Replace("\"", "");
|
||||
var payloadParts = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(rightSignature)).Split('?');
|
||||
|
||||
if (GetHashBase64(payloadParts[1] + secret) == payloadParts[0])
|
||||
{
|
||||
//Sig correct
|
||||
return JsonConvert.DeserializeObject<T>(payloadParts[1]);
|
||||
return JsonConvert.DeserializeObject<T>(payloadParts[1]); //Sig correct
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
catch (Exception) { }
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
private static string GetHashBase64(string str)
|
||||
{
|
||||
using var sha256 = SHA256.Create();
|
||||
|
||||
return Convert.ToBase64String(sha256.ComputeHash(Encoding.UTF8.GetBytes(str)));
|
||||
}
|
||||
|
||||
private static string GetHashBase64MD5(string str)
|
||||
{
|
||||
using var md5 = MD5.Create();
|
||||
|
||||
return Convert.ToBase64String(md5.ComputeHash(Encoding.UTF8.GetBytes(str)));
|
||||
}
|
||||
}
|
||||
}
|
@ -23,57 +23,27 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Utils
|
||||
{
|
||||
namespace ASC.Common.Utils;
|
||||
|
||||
[Singletone]
|
||||
public class TimeZoneConverter
|
||||
{
|
||||
private TimeZoneInfo defaultTimeZone;
|
||||
|
||||
private TimeZoneInfo _defaultTimeZone;
|
||||
private IEnumerable<MapZone> _mapZones;
|
||||
|
||||
private bool _customMode;
|
||||
private bool _isMono;
|
||||
|
||||
private Dictionary<string, string> _translations;
|
||||
|
||||
private IConfiguration Configuration { get; }
|
||||
private ILog Log { get; }
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly ILog _logger;
|
||||
|
||||
public TimeZoneConverter(IConfiguration configuration, IOptionsMonitor<ILog> option)
|
||||
{
|
||||
Log = option.CurrentValue;
|
||||
Configuration = configuration;
|
||||
_logger = option.CurrentValue;
|
||||
_configuration = configuration;
|
||||
InitMapZones();
|
||||
InitTranslations();
|
||||
}
|
||||
|
||||
private void InitMapZones()
|
||||
{
|
||||
try
|
||||
{
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
|
||||
using var stream = assembly.GetManifestResourceStream("ASC.Common.Utils.TimeZoneConverter.windowsZones.xml");
|
||||
var xml = XElement.Load(stream);
|
||||
_mapZones = from row in xml.XPathSelectElements("*/mapTimezones/mapZone")
|
||||
let olsonTimeZones = row.Attribute("type").Value.Split(' ')
|
||||
from olsonTimeZone in olsonTimeZones
|
||||
select new MapZone
|
||||
{
|
||||
OlsonTimeZoneId = olsonTimeZone,
|
||||
WindowsTimeZoneId = row.Attribute("other").Value,
|
||||
Territory = row.Attribute("territory").Value
|
||||
};
|
||||
_mapZones = _mapZones.ToList();
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
_mapZones = new MapZone[0];
|
||||
Log.Error(error);
|
||||
}
|
||||
}
|
||||
|
||||
public string GetTimeZoneDisplayName(TimeZoneInfo tz)
|
||||
{
|
||||
var displayName = GetTimeZoneName(tz);
|
||||
@ -99,14 +69,18 @@ namespace ASC.Common.Utils
|
||||
var mapZone = GetMapZoneByWindowsTzId(olsonTimeZoneId);
|
||||
|
||||
if (mapZone != null)
|
||||
{
|
||||
return olsonTimeZoneId; //already Windows
|
||||
}
|
||||
|
||||
mapZone = GetMapZoneByOlsonTzId(olsonTimeZoneId);
|
||||
|
||||
if (mapZone != null)
|
||||
{
|
||||
return mapZone.WindowsTimeZoneId;
|
||||
}
|
||||
|
||||
Log.Error("OlsonTimeZone " + olsonTimeZoneId + " not found");
|
||||
_logger.Error($"OlsonTimeZone {olsonTimeZoneId} not found");
|
||||
|
||||
return defaultIfNoMatch ? "UTC" : null;
|
||||
}
|
||||
@ -116,14 +90,18 @@ namespace ASC.Common.Utils
|
||||
var mapZone = GetMapZoneByOlsonTzId(windowsTimeZoneId);
|
||||
|
||||
if (mapZone != null)
|
||||
{
|
||||
return windowsTimeZoneId; //already Olson
|
||||
}
|
||||
|
||||
mapZone = GetMapZoneByWindowsTzId(windowsTimeZoneId);
|
||||
|
||||
if (mapZone != null)
|
||||
{
|
||||
return mapZone.OlsonTimeZoneId;
|
||||
}
|
||||
|
||||
Log.Error("WindowsTimeZone " + windowsTimeZoneId + " not found");
|
||||
_logger.Error($"WindowsTimeZone {windowsTimeZoneId} not found");
|
||||
|
||||
return defaultIfNoMatch ? "Etc/GMT" : null;
|
||||
}
|
||||
@ -146,35 +124,37 @@ namespace ASC.Common.Utils
|
||||
try
|
||||
{
|
||||
var mapZone = GetMapZoneByOlsonTzId(timeZoneId);
|
||||
|
||||
if (mapZone != null)
|
||||
{
|
||||
return TimeZoneInfo.FindSystemTimeZoneById(mapZone.WindowsTimeZoneId);
|
||||
}
|
||||
|
||||
mapZone = GetMapZoneByWindowsTzId(timeZoneId);
|
||||
|
||||
if (mapZone != null)
|
||||
{
|
||||
return TimeZoneInfo.FindSystemTimeZoneById(mapZone.OlsonTimeZoneId);
|
||||
}
|
||||
|
||||
Log.InfoFormat("TimeZone {0} not found", timeZoneId);
|
||||
_logger.InfoFormat("TimeZone {0} not found", timeZoneId);
|
||||
|
||||
return defaultIfNoMatch ? GetTimeZoneByOffset(timeZoneId) ?? defaultTimezone : null;
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
Log.Error(error);
|
||||
_logger.Error(error);
|
||||
|
||||
return defaultIfNoMatch ? defaultTimezone : null;
|
||||
}
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
Log.Error(error);
|
||||
_logger.Error(error);
|
||||
|
||||
return defaultIfNoMatch ? defaultTimezone : null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private MapZone GetMapZoneByOlsonTzId(string olsonTimeZoneId)
|
||||
{
|
||||
return _mapZones.FirstOrDefault(x =>
|
||||
@ -197,59 +177,69 @@ namespace ASC.Common.Utils
|
||||
tz.StandardName == timeZoneId ||
|
||||
tz.DaylightName == timeZoneId);
|
||||
|
||||
if (timeZone != null) return timeZone;
|
||||
if (timeZone != null)
|
||||
{
|
||||
return timeZone;
|
||||
}
|
||||
|
||||
var regex = new Regex(@"[+-][0-9]{2}:[0-9]{2}\b");
|
||||
|
||||
var offsetStr = regex.Match(timeZoneId).Value.TrimStart('+');
|
||||
|
||||
if (string.IsNullOrEmpty(offsetStr)) return null;
|
||||
|
||||
if (string.IsNullOrEmpty(offsetStr))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!TimeSpan.TryParse(offsetStr, out var offset))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return systemTimeZones.FirstOrDefault(tz => tz.BaseUtcOffset == offset);
|
||||
}
|
||||
|
||||
private void InitTranslations()
|
||||
private void InitMapZones()
|
||||
{
|
||||
try
|
||||
{
|
||||
_customMode = Configuration["core:custom-mode"] == "true";
|
||||
|
||||
if (!_customMode)
|
||||
{
|
||||
_translations = new Dictionary<string, string>();
|
||||
return;
|
||||
}
|
||||
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
|
||||
using var stream = assembly.GetManifestResourceStream("ASC.Common.Utils.TimeZoneConverter.timeZoneNames.xml");
|
||||
using var stream = assembly.GetManifestResourceStream("ASC.Common.Utils.TimeZoneConverter.windowsZones.xml");
|
||||
|
||||
var xml = XElement.Load(stream);
|
||||
_translations = (from row in xml.XPathSelectElements("*/zone")
|
||||
select new KeyValuePair<string, string>(row.Attribute("type").Value, row.Value)
|
||||
).ToDictionary(item => item.Key, item => item.Value);
|
||||
|
||||
_mapZones = from row in xml.XPathSelectElements("*/mapTimezones/mapZone")
|
||||
let olsonTimeZones = row.Attribute("type").Value.Split(' ')
|
||||
from olsonTimeZone in olsonTimeZones
|
||||
select new MapZone
|
||||
{
|
||||
OlsonTimeZoneId = olsonTimeZone,
|
||||
WindowsTimeZoneId = row.Attribute("other").Value,
|
||||
Territory = row.Attribute("territory").Value
|
||||
};
|
||||
|
||||
_mapZones = _mapZones.ToList();
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
_translations = new Dictionary<string, string>();
|
||||
Log.Error(error);
|
||||
_mapZones = new MapZone[0];
|
||||
_logger.Error(error);
|
||||
}
|
||||
}
|
||||
|
||||
public string GetTimeZoneName(TimeZoneInfo timeZone)
|
||||
{
|
||||
if (!_customMode)
|
||||
{
|
||||
return _isMono ? timeZone.Id : timeZone.DisplayName;
|
||||
}
|
||||
|
||||
return _translations.ContainsKey(timeZone.Id) ? _translations[timeZone.Id] : timeZone.DisplayName;
|
||||
}
|
||||
|
||||
private TimeZoneInfo GetTimeZoneDefault()
|
||||
{
|
||||
if (defaultTimeZone == null)
|
||||
if (_defaultTimeZone == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -263,6 +253,7 @@ namespace ASC.Common.Utils
|
||||
else
|
||||
{
|
||||
var id = string.Empty;
|
||||
|
||||
if (File.Exists("/etc/timezone"))
|
||||
{
|
||||
_isMono = true;
|
||||
@ -278,29 +269,62 @@ namespace ASC.Common.Utils
|
||||
RedirectStandardOutput = true,
|
||||
UseShellExecute = false,
|
||||
};
|
||||
|
||||
using var p = Process.Start(psi);
|
||||
|
||||
if (p.WaitForExit(1000))
|
||||
{
|
||||
id = p.StandardOutput.ReadToEnd();
|
||||
}
|
||||
|
||||
p.Close();
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(id))
|
||||
{
|
||||
tz = TimeZoneInfo.GetSystemTimeZones().FirstOrDefault(z => z.Id == id) ?? tz;
|
||||
}
|
||||
}
|
||||
}
|
||||
defaultTimeZone = tz;
|
||||
|
||||
_defaultTimeZone = tz;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignore
|
||||
defaultTimeZone = TimeZoneInfo.Utc;
|
||||
_defaultTimeZone = TimeZoneInfo.Utc;
|
||||
}
|
||||
}
|
||||
|
||||
return defaultTimeZone;
|
||||
return _defaultTimeZone;
|
||||
}
|
||||
|
||||
private void InitTranslations()
|
||||
{
|
||||
try
|
||||
{
|
||||
_customMode = _configuration["core:custom-mode"] == "true";
|
||||
|
||||
if (!_customMode)
|
||||
{
|
||||
_translations = new Dictionary<string, string>();
|
||||
return;
|
||||
}
|
||||
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
|
||||
using var stream = assembly.GetManifestResourceStream("ASC.Common.Utils.TimeZoneConverter.timeZoneNames.xml");
|
||||
|
||||
var xml = XElement.Load(stream);
|
||||
_translations = (from row in xml.XPathSelectElements("*/zone")
|
||||
select new KeyValuePair<string, string>(row.Attribute("type").Value, row.Value)
|
||||
).ToDictionary(item => item.Key, item => item.Value);
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
_translations = new Dictionary<string, string>();
|
||||
_logger.Error(error);
|
||||
}
|
||||
}
|
||||
|
||||
private class MapZone
|
||||
@ -310,4 +334,3 @@ namespace ASC.Common.Utils
|
||||
public string Territory { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
@ -23,8 +23,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Utils
|
||||
{
|
||||
namespace ASC.Common.Utils;
|
||||
|
||||
public class TextLoader : ResourceLoader
|
||||
{
|
||||
public override void Init(Commons.Collections.ExtendedProperties configuration)
|
||||
@ -50,20 +50,24 @@ namespace ASC.Common.Utils
|
||||
|
||||
public static class VelocityFormatter
|
||||
{
|
||||
private static bool initialized;
|
||||
private static readonly ConcurrentDictionary<string, Template> patterns = new ConcurrentDictionary<string, Template>();
|
||||
private static bool _initialized;
|
||||
private static readonly ConcurrentDictionary<string, Template> _patterns = new ConcurrentDictionary<string, Template>();
|
||||
|
||||
public static string FormatText(string templateText, IDictionary<string, object> values)
|
||||
{
|
||||
var nvelocityContext = new VelocityContext();
|
||||
|
||||
foreach (var tagValue in values)
|
||||
{
|
||||
nvelocityContext.Put(tagValue.Key, tagValue.Value);
|
||||
}
|
||||
|
||||
return FormatText(templateText, nvelocityContext);
|
||||
}
|
||||
|
||||
public static string FormatText(string templateText, VelocityContext context)
|
||||
{
|
||||
if (!initialized)
|
||||
if (!_initialized)
|
||||
{
|
||||
var properties = new Commons.Collections.ExtendedProperties();
|
||||
properties.AddProperty("resource.loader", "custom");
|
||||
@ -71,18 +75,20 @@ namespace ASC.Common.Utils
|
||||
properties.AddProperty("input.encoding", Encoding.UTF8.WebName);
|
||||
properties.AddProperty("output.encoding", Encoding.UTF8.WebName);
|
||||
Velocity.Init(properties);
|
||||
initialized = true;
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
using var writer = new StringWriter();
|
||||
var key = templateText.GetHashCode().ToString();
|
||||
if (!patterns.TryGetValue(key, out var template))
|
||||
|
||||
if (!_patterns.TryGetValue(key, out var template))
|
||||
{
|
||||
template = Velocity.GetTemplate(templateText);
|
||||
patterns.TryAdd(key, template);
|
||||
_patterns.TryAdd(key, template);
|
||||
}
|
||||
|
||||
template.Merge(context, writer);
|
||||
|
||||
return writer.GetStringBuilder().ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,8 +24,8 @@
|
||||
*/
|
||||
|
||||
|
||||
namespace ASC.Common.Utils
|
||||
{
|
||||
namespace ASC.Common.Utils;
|
||||
|
||||
public static class Wildcard
|
||||
{
|
||||
public static bool WildcardMatch(this string input, string pattern)
|
||||
@ -39,6 +39,7 @@ namespace ASC.Common.Utils
|
||||
{
|
||||
return IsMatch(pattern, input, ignoreCase);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -62,17 +63,25 @@ namespace ASC.Common.Utils
|
||||
break;
|
||||
case '*':
|
||||
isAsterix = true;
|
||||
|
||||
while (i < pattern.Length &&
|
||||
pattern[i] == '*')
|
||||
{
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i >= pattern.Length)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
continue;
|
||||
default:
|
||||
if (offsetInput >= input.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((ignoreCase
|
||||
? char.ToLower(input[offsetInput])
|
||||
: input[offsetInput])
|
||||
@ -82,8 +91,12 @@ namespace ASC.Common.Utils
|
||||
: pattern[i]))
|
||||
{
|
||||
if (!isAsterix)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
offsetInput++;
|
||||
|
||||
continue;
|
||||
}
|
||||
offsetInput++;
|
||||
@ -93,12 +106,15 @@ namespace ASC.Common.Utils
|
||||
}
|
||||
|
||||
if (i > input.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
while (i < pattern.Length && pattern[i] == '*')
|
||||
{
|
||||
++i;
|
||||
}
|
||||
|
||||
return offsetInput == input.Length;
|
||||
}
|
||||
}
|
||||
}
|
@ -23,44 +23,38 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Web
|
||||
{
|
||||
namespace ASC.Common.Web;
|
||||
|
||||
public abstract class AbstractHttpAsyncHandler // : IHttpAsyncHandler, IReadOnlySessionState
|
||||
{
|
||||
private Action<Microsoft.AspNetCore.Http.HttpContext> processRequest;
|
||||
private IPrincipal principal;
|
||||
private CultureInfo culture;
|
||||
public bool IsReusable => false;
|
||||
|
||||
private Action<HttpContext> _processRequest;
|
||||
private IPrincipal _principal;
|
||||
private CultureInfo _culture;
|
||||
|
||||
public bool IsReusable
|
||||
public void ProcessRequest(HttpContext context)
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
|
||||
public void ProcessRequest(Microsoft.AspNetCore.Http.HttpContext context)
|
||||
{
|
||||
Thread.CurrentThread.CurrentCulture = culture;
|
||||
Thread.CurrentThread.CurrentUICulture = culture;
|
||||
Thread.CurrentPrincipal = principal;
|
||||
Thread.CurrentThread.CurrentCulture = _culture;
|
||||
Thread.CurrentThread.CurrentUICulture = _culture;
|
||||
Thread.CurrentPrincipal = _principal;
|
||||
//HttpContext.Current = context;
|
||||
OnProcessRequest(context);
|
||||
}
|
||||
|
||||
public IAsyncResult BeginProcessRequest(Microsoft.AspNetCore.Http.HttpContext context, AsyncCallback cb, object extraData)
|
||||
public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
|
||||
{
|
||||
culture = Thread.CurrentThread.CurrentCulture;
|
||||
principal = Thread.CurrentPrincipal;
|
||||
processRequest = ProcessRequest;
|
||||
return processRequest.BeginInvoke(context, cb, extraData);
|
||||
_culture = Thread.CurrentThread.CurrentCulture;
|
||||
_principal = Thread.CurrentPrincipal;
|
||||
_processRequest = ProcessRequest;
|
||||
|
||||
return _processRequest.BeginInvoke(context, cb, extraData);
|
||||
}
|
||||
|
||||
public void EndProcessRequest(IAsyncResult result)
|
||||
{
|
||||
processRequest.EndInvoke(result);
|
||||
_processRequest.EndInvoke(result);
|
||||
}
|
||||
|
||||
|
||||
public abstract void OnProcessRequest(Microsoft.AspNetCore.Http.HttpContext context);
|
||||
}
|
||||
public abstract void OnProcessRequest(HttpContext context);
|
||||
}
|
||||
|
@ -23,31 +23,27 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Web;
|
||||
|
||||
#region usings
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
namespace ASC.Common.Web
|
||||
{
|
||||
public class DisposableHttpContext : IDisposable
|
||||
{
|
||||
private const string key = "disposable.key";
|
||||
private readonly Microsoft.AspNetCore.Http.HttpContext ctx;
|
||||
|
||||
public DisposableHttpContext(Microsoft.AspNetCore.Http.HttpContext ctx)
|
||||
{
|
||||
this.ctx = ctx ?? throw new ArgumentNullException();
|
||||
}
|
||||
private const string Key = "disposable.key";
|
||||
|
||||
public object this[string key]
|
||||
{
|
||||
get { return Items.ContainsKey(key) ? Items[key] : null; }
|
||||
get => Items.ContainsKey(key) ? Items[key] : null;
|
||||
set
|
||||
{
|
||||
if (value == null) throw new ArgumentNullException();
|
||||
if (!(value is IDisposable)) throw new ArgumentException("Only IDisposable may be added!");
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException();
|
||||
}
|
||||
|
||||
if (!(value is IDisposable))
|
||||
{
|
||||
throw new ArgumentException("Only IDisposable may be added!");
|
||||
}
|
||||
|
||||
Items[key] = (IDisposable)value;
|
||||
}
|
||||
}
|
||||
@ -56,20 +52,26 @@ namespace ASC.Common.Web
|
||||
{
|
||||
get
|
||||
{
|
||||
var table = (Dictionary<string, IDisposable>)ctx.Items[key];
|
||||
var table = (Dictionary<string, IDisposable>)_context.Items[Key];
|
||||
|
||||
if (table == null)
|
||||
{
|
||||
table = new Dictionary<string, IDisposable>(1);
|
||||
ctx.Items.Add(key, table);
|
||||
_context.Items.Add(Key, table);
|
||||
}
|
||||
|
||||
return table;
|
||||
}
|
||||
}
|
||||
|
||||
#region IDisposable Members
|
||||
|
||||
private readonly HttpContext _context;
|
||||
private bool _isDisposed;
|
||||
|
||||
public DisposableHttpContext(HttpContext ctx)
|
||||
{
|
||||
_context = ctx ?? throw new ArgumentNullException(nameof(ctx));
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!_isDisposed)
|
||||
@ -80,14 +82,10 @@ namespace ASC.Common.Web
|
||||
{
|
||||
item.Dispose();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
_isDisposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -1,7 +1,9 @@
|
||||
namespace ASC.Common.Web
|
||||
{
|
||||
namespace ASC.Common.Web;
|
||||
|
||||
public class HttpException : Exception
|
||||
{
|
||||
public int StatusCode { get; }
|
||||
|
||||
public HttpException(int httpStatusCode)
|
||||
{
|
||||
StatusCode = httpStatusCode;
|
||||
@ -31,7 +33,4 @@
|
||||
{
|
||||
StatusCode = (int)httpStatusCode;
|
||||
}
|
||||
|
||||
public int StatusCode { get; }
|
||||
}
|
||||
}
|
@ -1,19 +1,11 @@
|
||||
namespace ASC.Common.Web
|
||||
{
|
||||
namespace ASC.Common.Web;
|
||||
|
||||
[Serializable]
|
||||
public class ItemNotFoundException : HttpException
|
||||
{
|
||||
public ItemNotFoundException() : base(404, "Not found") { }
|
||||
|
||||
public ItemNotFoundException() : base(404, "Not found")
|
||||
{
|
||||
}
|
||||
public ItemNotFoundException(string message) : base(404, message) { }
|
||||
|
||||
public ItemNotFoundException(string message) : base(404, message)
|
||||
{
|
||||
}
|
||||
|
||||
public ItemNotFoundException(string message, Exception inner) : base(404, message, inner)
|
||||
{
|
||||
}
|
||||
}
|
||||
public ItemNotFoundException(string message, Exception inner) : base(404, message, inner) { }
|
||||
}
|
||||
|
@ -23,13 +23,12 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Web
|
||||
{
|
||||
namespace ASC.Common.Web;
|
||||
|
||||
public static class MimeMapping
|
||||
{
|
||||
private static readonly Hashtable extensionToMimeMappingTable = new Hashtable(200, StringComparer.CurrentCultureIgnoreCase);
|
||||
|
||||
private static readonly IDictionary<string, IList<string>> mimeSynonyms = new Dictionary<string, IList<string>>();
|
||||
private static readonly Hashtable _extensionToMimeMappingTable = new Hashtable(200, StringComparer.CurrentCultureIgnoreCase);
|
||||
private static readonly IDictionary<string, IList<string>> _mimeSynonyms = new Dictionary<string, IList<string>>();
|
||||
|
||||
static MimeMapping()
|
||||
{
|
||||
@ -807,56 +806,48 @@ namespace ASC.Common.Web
|
||||
AddMimeMapping(".*", "application/octet-stream");
|
||||
}
|
||||
|
||||
private static void AddMimeMapping(string extension, string MimeType)
|
||||
public static string GetExtention(string mimeMapping)
|
||||
{
|
||||
if (extensionToMimeMappingTable.ContainsKey(extension))
|
||||
if (string.IsNullOrEmpty(mimeMapping)) return null;
|
||||
|
||||
foreach (DictionaryEntry entry in _extensionToMimeMappingTable)
|
||||
{
|
||||
AddMimeSynonym((string)extensionToMimeMappingTable[extension], MimeType);
|
||||
}
|
||||
else
|
||||
{
|
||||
extensionToMimeMappingTable.Add(extension, MimeType);
|
||||
}
|
||||
var mime = (string)entry.Value;
|
||||
if (mime.Equals(mimeMapping, StringComparison.OrdinalIgnoreCase)) return (string)entry.Key;
|
||||
if (!_mimeSynonyms.ContainsKey(mime)) continue;
|
||||
if (_mimeSynonyms[mime].Contains(mimeMapping.ToLowerInvariant())) return (string)entry.Key;
|
||||
}
|
||||
|
||||
private static void AddMimeSynonym(string mime, string synonym)
|
||||
{
|
||||
if (!mimeSynonyms.ContainsKey(mime))
|
||||
{
|
||||
mimeSynonyms[mime] = new List<string>();
|
||||
}
|
||||
if (!mimeSynonyms[mime].Contains(synonym))
|
||||
{
|
||||
mimeSynonyms[mime].Add(synonym);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static string GetMimeMapping(string fileName)
|
||||
{
|
||||
string str = null;
|
||||
var startIndex = fileName.LastIndexOf('.');
|
||||
|
||||
if (0 <= startIndex && fileName.LastIndexOf('\\') < startIndex)
|
||||
{
|
||||
str = (string)extensionToMimeMappingTable[fileName.Substring(startIndex)];
|
||||
}
|
||||
if (str == null)
|
||||
{
|
||||
str = (string)extensionToMimeMappingTable[".*"];
|
||||
}
|
||||
str = (string)_extensionToMimeMappingTable[fileName.Substring(startIndex)];
|
||||
|
||||
if (str == null) str = (string)_extensionToMimeMappingTable[".*"];
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
public static string GetExtention(string mimeMapping)
|
||||
private static void AddMimeMapping(string extension, string MimeType)
|
||||
{
|
||||
if (string.IsNullOrEmpty(mimeMapping)) return null;
|
||||
foreach (DictionaryEntry entry in extensionToMimeMappingTable)
|
||||
if (_extensionToMimeMappingTable.ContainsKey(extension))
|
||||
AddMimeSynonym((string)_extensionToMimeMappingTable[extension], MimeType);
|
||||
|
||||
else _extensionToMimeMappingTable.Add(extension, MimeType);
|
||||
}
|
||||
|
||||
private static void AddMimeSynonym(string mime, string synonym)
|
||||
{
|
||||
var mime = (string)entry.Value;
|
||||
if (mime.Equals(mimeMapping, StringComparison.OrdinalIgnoreCase)) return (string)entry.Key;
|
||||
if (!mimeSynonyms.ContainsKey(mime)) continue;
|
||||
if (mimeSynonyms[mime].Contains(mimeMapping.ToLowerInvariant())) return (string)entry.Key;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (!_mimeSynonyms.ContainsKey(mime))
|
||||
_mimeSynonyms[mime] = new List<string>();
|
||||
|
||||
if (!_mimeSynonyms[mime].Contains(synonym))
|
||||
_mimeSynonyms[mime].Add(synonym);
|
||||
}
|
||||
}
|
@ -23,19 +23,16 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Common.Web
|
||||
{
|
||||
namespace ASC.Common.Web;
|
||||
|
||||
public class RouteCallInfo
|
||||
{
|
||||
public int? Tid { get; set; }
|
||||
public string Url { get; set; }
|
||||
public string AttachmentUrl { get; set; }
|
||||
public bool IsNewRequest { get; set; }
|
||||
|
||||
public string Method { get; set; }
|
||||
|
||||
public Dictionary<string, object> Params { get; set; }
|
||||
|
||||
public bool CleanupHtml { get; set; }
|
||||
|
||||
public RouteCallInfo()
|
||||
@ -45,7 +42,8 @@ namespace ASC.Common.Web
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("{0} {1} T:{2},{3}", Method.ToUpper(), Url, Tid, string.Join(",", Params.Select(x => string.Format("{0}={1}", x.Key, x.Value)).ToArray()));
|
||||
}
|
||||
return string.Format("{0} {1} T:{2},{3}", Method.ToUpper(), Url, Tid,
|
||||
string.Join(",",
|
||||
Params.Select(x => string.Format("{0}={1}", x.Key, x.Value)).ToArray()));
|
||||
}
|
||||
}
|
@ -1,16 +1,19 @@
|
||||
namespace ASC.Common.Web
|
||||
{
|
||||
namespace ASC.Common.Web;
|
||||
|
||||
public static class VirtualPathUtility
|
||||
{
|
||||
public static string ToAbsolute(string virtualPath)
|
||||
{
|
||||
if (string.IsNullOrEmpty(virtualPath))
|
||||
{
|
||||
return virtualPath;
|
||||
}
|
||||
|
||||
if (Uri.IsWellFormedUriString(virtualPath, UriKind.Absolute))
|
||||
{
|
||||
return virtualPath;
|
||||
}
|
||||
|
||||
return "/" + virtualPath.TrimStart('~', '/');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ namespace ASC.Core.Billing
|
||||
Cache.Remove(TariffService.GetTariffCacheKey(i.TenantId));
|
||||
Cache.Remove(TariffService.GetBillingUrlCacheKey(i.TenantId));
|
||||
Cache.Remove(TariffService.GetBillingPaymentCacheKey(i.TenantId)); // clear all payments
|
||||
}, CacheNotifyAction.Remove);
|
||||
}, ASC.Common.Caching.CacheNotifyAction.Remove);
|
||||
|
||||
//TODO: Change code of WCF -> not supported in .NET standard/.Net Core
|
||||
/*try
|
||||
@ -297,7 +297,7 @@ namespace ASC.Core.Billing
|
||||
|
||||
public void ClearCache(int tenantId)
|
||||
{
|
||||
Notify.Publish(new TariffCacheItem { TenantId = tenantId }, CacheNotifyAction.Remove);
|
||||
Notify.Publish(new TariffCacheItem { TenantId = tenantId }, ASC.Common.Caching.CacheNotifyAction.Remove);
|
||||
}
|
||||
|
||||
public IEnumerable<PaymentInfo> GetPayments(int tenantId)
|
||||
|
@ -36,8 +36,8 @@ namespace ASC.Core.Caching
|
||||
CacheNotify = cacheNotify;
|
||||
Cache = cache;
|
||||
|
||||
cacheNotify.Subscribe((r) => UpdateCache(r, true), CacheNotifyAction.Remove);
|
||||
cacheNotify.Subscribe((r) => UpdateCache(r, false), CacheNotifyAction.InsertOrUpdate);
|
||||
cacheNotify.Subscribe((r) => UpdateCache(r, true), ASC.Common.Caching.CacheNotifyAction.Remove);
|
||||
cacheNotify.Subscribe((r) => UpdateCache(r, false), ASC.Common.Caching.CacheNotifyAction.InsertOrUpdate);
|
||||
}
|
||||
|
||||
private void UpdateCache(AzRecord r, bool remove)
|
||||
@ -102,14 +102,14 @@ namespace ASC.Core.Caching
|
||||
public AzRecord SaveAce(int tenant, AzRecord r)
|
||||
{
|
||||
r = service.SaveAce(tenant, r);
|
||||
cacheNotify.Publish(r, CacheNotifyAction.InsertOrUpdate);
|
||||
cacheNotify.Publish(r, ASC.Common.Caching.CacheNotifyAction.InsertOrUpdate);
|
||||
return r;
|
||||
}
|
||||
|
||||
public void RemoveAce(int tenant, AzRecord r)
|
||||
{
|
||||
service.RemoveAce(tenant, r);
|
||||
cacheNotify.Publish(r, CacheNotifyAction.Remove);
|
||||
cacheNotify.Publish(r, ASC.Common.Caching.CacheNotifyAction.Remove);
|
||||
}
|
||||
}
|
||||
}
|
@ -62,7 +62,7 @@ namespace ASC.Core.Caching
|
||||
{
|
||||
Cache.Remove(i.Key);
|
||||
}
|
||||
}, CacheNotifyAction.Any);
|
||||
}, ASC.Common.Caching.CacheNotifyAction.Any);
|
||||
}
|
||||
}
|
||||
|
||||
@ -142,7 +142,7 @@ namespace ASC.Core.Caching
|
||||
public TenantQuota SaveTenantQuota(TenantQuota quota)
|
||||
{
|
||||
var q = Service.SaveTenantQuota(quota);
|
||||
CacheNotify.Publish(new QuotaCacheItem { Key = QuotaServiceCache.KEY_QUOTA }, CacheNotifyAction.Any);
|
||||
CacheNotify.Publish(new QuotaCacheItem { Key = QuotaServiceCache.KEY_QUOTA }, ASC.Common.Caching.CacheNotifyAction.Any);
|
||||
return q;
|
||||
}
|
||||
|
||||
@ -155,7 +155,7 @@ namespace ASC.Core.Caching
|
||||
public void SetTenantQuotaRow(TenantQuotaRow row, bool exchange)
|
||||
{
|
||||
Service.SetTenantQuotaRow(row, exchange);
|
||||
CacheNotify.Publish(new QuotaCacheItem { Key = GetKey(row.Tenant) }, CacheNotifyAction.InsertOrUpdate);
|
||||
CacheNotify.Publish(new QuotaCacheItem { Key = GetKey(row.Tenant) }, ASC.Common.Caching.CacheNotifyAction.InsertOrUpdate);
|
||||
}
|
||||
|
||||
public IEnumerable<TenantQuotaRow> FindTenantQuotaRows(int tenantId)
|
||||
|
@ -48,7 +48,7 @@ namespace ASC.Core.Caching
|
||||
store.SaveSubscription(s);
|
||||
}
|
||||
}
|
||||
}, CacheNotifyAction.InsertOrUpdate);
|
||||
}, ASC.Common.Caching.CacheNotifyAction.InsertOrUpdate);
|
||||
|
||||
notifyRecord.Subscribe((s) =>
|
||||
{
|
||||
@ -67,7 +67,7 @@ namespace ASC.Core.Caching
|
||||
}
|
||||
}
|
||||
}
|
||||
}, CacheNotifyAction.Remove);
|
||||
}, ASC.Common.Caching.CacheNotifyAction.Remove);
|
||||
|
||||
notifyMethod.Subscribe((m) =>
|
||||
{
|
||||
@ -79,7 +79,7 @@ namespace ASC.Core.Caching
|
||||
store.SetSubscriptionMethod(m);
|
||||
}
|
||||
}
|
||||
}, CacheNotifyAction.Any);
|
||||
}, ASC.Common.Caching.CacheNotifyAction.Any);
|
||||
}
|
||||
|
||||
private SubsciptionsStore GetSubsciptionsStore(int tenant, string sourceId, string actionId)
|
||||
@ -153,19 +153,19 @@ namespace ASC.Core.Caching
|
||||
public void SaveSubscription(SubscriptionRecord s)
|
||||
{
|
||||
service.SaveSubscription(s);
|
||||
notifyRecord.Publish(s, CacheNotifyAction.InsertOrUpdate);
|
||||
notifyRecord.Publish(s, ASC.Common.Caching.CacheNotifyAction.InsertOrUpdate);
|
||||
}
|
||||
|
||||
public void RemoveSubscriptions(int tenant, string sourceId, string actionId)
|
||||
{
|
||||
service.RemoveSubscriptions(tenant, sourceId, actionId);
|
||||
notifyRecord.Publish(new SubscriptionRecord { Tenant = tenant, SourceId = sourceId, ActionId = actionId }, CacheNotifyAction.Remove);
|
||||
notifyRecord.Publish(new SubscriptionRecord { Tenant = tenant, SourceId = sourceId, ActionId = actionId }, ASC.Common.Caching.CacheNotifyAction.Remove);
|
||||
}
|
||||
|
||||
public void RemoveSubscriptions(int tenant, string sourceId, string actionId, string objectId)
|
||||
{
|
||||
service.RemoveSubscriptions(tenant, sourceId, actionId, objectId);
|
||||
notifyRecord.Publish(new SubscriptionRecord { Tenant = tenant, SourceId = sourceId, ActionId = actionId, ObjectId = objectId }, CacheNotifyAction.Remove);
|
||||
notifyRecord.Publish(new SubscriptionRecord { Tenant = tenant, SourceId = sourceId, ActionId = actionId, ObjectId = objectId }, ASC.Common.Caching.CacheNotifyAction.Remove);
|
||||
}
|
||||
|
||||
public IEnumerable<SubscriptionMethod> GetSubscriptionMethods(int tenant, string sourceId, string actionId, string recipientId)
|
||||
@ -180,7 +180,7 @@ namespace ASC.Core.Caching
|
||||
public void SetSubscriptionMethod(SubscriptionMethod m)
|
||||
{
|
||||
service.SetSubscriptionMethod(m);
|
||||
notifyMethod.Publish(m, CacheNotifyAction.Any);
|
||||
notifyMethod.Publish(m, ASC.Common.Caching.CacheNotifyAction.Any);
|
||||
}
|
||||
|
||||
|
||||
|
@ -50,12 +50,12 @@ namespace ASC.Core.Caching
|
||||
var tenants = GetTenantStore();
|
||||
tenants.Remove(t.TenantId);
|
||||
tenants.Clear(coreBaseSettings);
|
||||
}, CacheNotifyAction.InsertOrUpdate);
|
||||
}, ASC.Common.Caching.CacheNotifyAction.InsertOrUpdate);
|
||||
|
||||
cacheNotifySettings.Subscribe((s) =>
|
||||
{
|
||||
Cache.Remove(s.Key);
|
||||
}, CacheNotifyAction.Remove);
|
||||
}, ASC.Common.Caching.CacheNotifyAction.Remove);
|
||||
}
|
||||
|
||||
internal TenantStore GetTenantStore()
|
||||
@ -267,14 +267,14 @@ namespace ASC.Core.Caching
|
||||
public Tenant SaveTenant(CoreSettings coreSettings, Tenant tenant)
|
||||
{
|
||||
tenant = Service.SaveTenant(coreSettings, tenant);
|
||||
CacheNotifyItem.Publish(new TenantCacheItem() { TenantId = tenant.TenantId }, CacheNotifyAction.InsertOrUpdate);
|
||||
CacheNotifyItem.Publish(new TenantCacheItem() { TenantId = tenant.TenantId }, ASC.Common.Caching.CacheNotifyAction.InsertOrUpdate);
|
||||
return tenant;
|
||||
}
|
||||
|
||||
public void RemoveTenant(int id, bool auto = false)
|
||||
{
|
||||
Service.RemoveTenant(id, auto);
|
||||
CacheNotifyItem.Publish(new TenantCacheItem() { TenantId = id }, CacheNotifyAction.InsertOrUpdate);
|
||||
CacheNotifyItem.Publish(new TenantCacheItem() { TenantId = id }, ASC.Common.Caching.CacheNotifyAction.InsertOrUpdate);
|
||||
}
|
||||
|
||||
public IEnumerable<TenantVersion> GetTenantVersions()
|
||||
@ -299,7 +299,7 @@ namespace ASC.Core.Caching
|
||||
{
|
||||
Service.SetTenantSettings(tenant, key, data);
|
||||
var cacheKey = string.Format("settings/{0}/{1}", tenant, key);
|
||||
CacheNotifySettings.Publish(new TenantSetting { Key = cacheKey }, CacheNotifyAction.Remove);
|
||||
CacheNotifySettings.Publish(new TenantSetting { Key = cacheKey }, ASC.Common.Caching.CacheNotifyAction.Remove);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -54,12 +54,12 @@ namespace ASC.Core.Caching
|
||||
CacheGroupCacheItem = cacheGroupCacheItem;
|
||||
CacheUserGroupRefItem = cacheUserGroupRefItem;
|
||||
|
||||
cacheUserInfoItem.Subscribe((u) => InvalidateCache(u), CacheNotifyAction.Any);
|
||||
cacheUserPhotoItem.Subscribe((p) => Cache.Remove(p.Key), CacheNotifyAction.Remove);
|
||||
cacheGroupCacheItem.Subscribe((g) => InvalidateCache(), CacheNotifyAction.Any);
|
||||
cacheUserInfoItem.Subscribe((u) => InvalidateCache(u), ASC.Common.Caching.CacheNotifyAction.Any);
|
||||
cacheUserPhotoItem.Subscribe((p) => Cache.Remove(p.Key), ASC.Common.Caching.CacheNotifyAction.Remove);
|
||||
cacheGroupCacheItem.Subscribe((g) => InvalidateCache(), ASC.Common.Caching.CacheNotifyAction.Any);
|
||||
|
||||
cacheUserGroupRefItem.Subscribe((r) => UpdateUserGroupRefCache(r, true), CacheNotifyAction.Remove);
|
||||
cacheUserGroupRefItem.Subscribe((r) => UpdateUserGroupRefCache(r, false), CacheNotifyAction.InsertOrUpdate);
|
||||
cacheUserGroupRefItem.Subscribe((r) => UpdateUserGroupRefCache(r, true), ASC.Common.Caching.CacheNotifyAction.Remove);
|
||||
cacheUserGroupRefItem.Subscribe((r) => UpdateUserGroupRefCache(r, false), ASC.Common.Caching.CacheNotifyAction.InsertOrUpdate);
|
||||
}
|
||||
|
||||
public void InvalidateCache()
|
||||
@ -261,14 +261,14 @@ namespace ASC.Core.Caching
|
||||
public UserInfo SaveUser(int tenant, UserInfo user)
|
||||
{
|
||||
user = Service.SaveUser(tenant, user);
|
||||
CacheUserInfoItem.Publish(new UserInfoCacheItem { Id = user.ID.ToString(), Tenant = tenant }, CacheNotifyAction.Any);
|
||||
CacheUserInfoItem.Publish(new UserInfoCacheItem { Id = user.ID.ToString(), Tenant = tenant }, ASC.Common.Caching.CacheNotifyAction.Any);
|
||||
return user;
|
||||
}
|
||||
|
||||
public void RemoveUser(int tenant, Guid id)
|
||||
{
|
||||
Service.RemoveUser(tenant, id);
|
||||
CacheUserInfoItem.Publish(new UserInfoCacheItem { Tenant = tenant, Id = id.ToString() }, CacheNotifyAction.Any);
|
||||
CacheUserInfoItem.Publish(new UserInfoCacheItem { Tenant = tenant, Id = id.ToString() }, ASC.Common.Caching.CacheNotifyAction.Any);
|
||||
}
|
||||
|
||||
public byte[] GetUserPhoto(int tenant, Guid id)
|
||||
@ -285,7 +285,7 @@ namespace ASC.Core.Caching
|
||||
public void SetUserPhoto(int tenant, Guid id, byte[] photo)
|
||||
{
|
||||
Service.SetUserPhoto(tenant, id, photo);
|
||||
CacheUserPhotoItem.Publish(new UserPhotoCacheItem { Key = UserServiceCache.GetUserPhotoCacheKey(tenant, id) }, CacheNotifyAction.Remove);
|
||||
CacheUserPhotoItem.Publish(new UserPhotoCacheItem { Key = UserServiceCache.GetUserPhotoCacheKey(tenant, id) }, ASC.Common.Caching.CacheNotifyAction.Remove);
|
||||
}
|
||||
|
||||
public DateTime GetUserPasswordStamp(int tenant, Guid id)
|
||||
@ -316,14 +316,14 @@ namespace ASC.Core.Caching
|
||||
public Group SaveGroup(int tenant, Group group)
|
||||
{
|
||||
group = Service.SaveGroup(tenant, group);
|
||||
CacheGroupCacheItem.Publish(new GroupCacheItem { Id = group.Id.ToString() }, CacheNotifyAction.Any);
|
||||
CacheGroupCacheItem.Publish(new GroupCacheItem { Id = group.Id.ToString() }, ASC.Common.Caching.CacheNotifyAction.Any);
|
||||
return group;
|
||||
}
|
||||
|
||||
public void RemoveGroup(int tenant, Guid id)
|
||||
{
|
||||
Service.RemoveGroup(tenant, id);
|
||||
CacheGroupCacheItem.Publish(new GroupCacheItem { Id = id.ToString() }, CacheNotifyAction.Any);
|
||||
CacheGroupCacheItem.Publish(new GroupCacheItem { Id = id.ToString() }, ASC.Common.Caching.CacheNotifyAction.Any);
|
||||
}
|
||||
|
||||
|
||||
@ -357,7 +357,7 @@ namespace ASC.Core.Caching
|
||||
public UserGroupRef SaveUserGroupRef(int tenant, UserGroupRef r)
|
||||
{
|
||||
r = Service.SaveUserGroupRef(tenant, r);
|
||||
CacheUserGroupRefItem.Publish(r, CacheNotifyAction.InsertOrUpdate);
|
||||
CacheUserGroupRefItem.Publish(r, ASC.Common.Caching.CacheNotifyAction.InsertOrUpdate);
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -366,7 +366,7 @@ namespace ASC.Core.Caching
|
||||
Service.RemoveUserGroupRef(tenant, userId, groupId, refType);
|
||||
|
||||
var r = new UserGroupRef(userId, groupId, refType) { Tenant = tenant };
|
||||
CacheUserGroupRefItem.Publish(r, CacheNotifyAction.Remove);
|
||||
CacheUserGroupRefItem.Publish(r, ASC.Common.Caching.CacheNotifyAction.Remove);
|
||||
}
|
||||
|
||||
|
||||
|
@ -170,7 +170,7 @@ namespace ASC.Core.Common.Configuration
|
||||
this[providerProp.Key] = null;
|
||||
}
|
||||
|
||||
Cache.Publish(new ConsumerCacheItem() { Name = this.Name }, CacheNotifyAction.Remove);
|
||||
Cache.Publish(new ConsumerCacheItem() { Name = this.Name }, ASC.Common.Caching.CacheNotifyAction.Remove);
|
||||
}
|
||||
|
||||
public bool Contains(KeyValuePair<string, string> item)
|
||||
|
@ -37,12 +37,12 @@ namespace ASC.Core.Data
|
||||
{
|
||||
Cache = cache;
|
||||
Notify = notify;
|
||||
Notify.Subscribe((i) => Cache.Remove(i.Key), CacheNotifyAction.Remove);
|
||||
Notify.Subscribe((i) => Cache.Remove(i.Key), ASC.Common.Caching.CacheNotifyAction.Remove);
|
||||
}
|
||||
|
||||
public void Remove(string key)
|
||||
{
|
||||
Notify.Publish(new SettingsCacheItem { Key = key }, CacheNotifyAction.Remove);
|
||||
Notify.Publish(new SettingsCacheItem { Key = key }, ASC.Common.Caching.CacheNotifyAction.Remove);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,8 @@
|
||||
public class PostgreSqlMessagesContext : MessagesContext { }
|
||||
public class MessagesContext : BaseDbContext
|
||||
{
|
||||
public DbSet<LoginEvents> LoginEvents { get; set; }
|
||||
public DbSet<AuditEvent> AuditEvents { get; set; }
|
||||
public DbSet<LoginEvent> LoginEvents { get; set; }
|
||||
public DbSet<User> Users { get; set; }
|
||||
|
||||
protected override Dictionary<Provider, Func<BaseDbContext>> ProviderContext
|
||||
@ -23,6 +24,7 @@
|
||||
{
|
||||
ModelBuilderWrapper
|
||||
.From(modelBuilder, Provider)
|
||||
.AddAuditEvent()
|
||||
.AddLoginEvents()
|
||||
.AddUser()
|
||||
.AddDbFunction();
|
||||
|
@ -40,7 +40,7 @@
|
||||
.HasColumnName("date")
|
||||
.HasColumnType("datetime");
|
||||
|
||||
entity.Property(e => e.Description)
|
||||
entity.Property(e => e.DescriptionRaw)
|
||||
.HasColumnName("description")
|
||||
.HasColumnType("varchar(20000)")
|
||||
.HasCharSet("utf8")
|
||||
@ -105,7 +105,7 @@
|
||||
|
||||
entity.Property(e => e.Date).HasColumnName("date");
|
||||
|
||||
entity.Property(e => e.Description)
|
||||
entity.Property(e => e.DescriptionRaw)
|
||||
.HasColumnName("description")
|
||||
.HasMaxLength(20000)
|
||||
.HasDefaultValueSql("NULL");
|
||||
|
@ -1,9 +1,10 @@
|
||||
namespace ASC.Core.Common.EF.Model
|
||||
{
|
||||
public class LoginEvents : MessageEvent
|
||||
public class LoginEvent : MessageEvent
|
||||
{
|
||||
public string Login { get; set; }
|
||||
}
|
||||
|
||||
public static class LoginEventsExtension
|
||||
{
|
||||
public static ModelBuilderWrapper AddLoginEvents(this ModelBuilderWrapper modelBuilder)
|
||||
@ -15,7 +16,7 @@
|
||||
}
|
||||
public static void MySqlAddLoginEvents(this ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.Entity<LoginEvents>(entity =>
|
||||
modelBuilder.Entity<LoginEvent>(entity =>
|
||||
{
|
||||
entity.ToTable("login_events");
|
||||
|
||||
@ -39,7 +40,7 @@
|
||||
.HasColumnName("date")
|
||||
.HasColumnType("datetime");
|
||||
|
||||
entity.Property(e => e.Description)
|
||||
entity.Property(e => e.DescriptionRaw)
|
||||
.HasColumnName("description")
|
||||
.HasColumnType("varchar(500)")
|
||||
.HasCharSet("utf8")
|
||||
@ -81,7 +82,7 @@
|
||||
}
|
||||
public static void PgSqlAddLoginEvents(this ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.Entity<LoginEvents>(entity =>
|
||||
modelBuilder.Entity<LoginEvent>(entity =>
|
||||
{
|
||||
entity.ToTable("login_events", "onlyoffice");
|
||||
|
||||
@ -102,7 +103,7 @@
|
||||
|
||||
entity.Property(e => e.Date).HasColumnName("date");
|
||||
|
||||
entity.Property(e => e.Description)
|
||||
entity.Property(e => e.DescriptionRaw)
|
||||
.HasColumnName("description")
|
||||
.HasMaxLength(500)
|
||||
.HasDefaultValueSql("NULL");
|
@ -11,6 +11,6 @@
|
||||
public Guid UserId { get; set; }
|
||||
public string Page { get; set; }
|
||||
public int Action { get; set; }
|
||||
public string Description { get; set; }
|
||||
public string DescriptionRaw { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
using CacheNotifyAction = ASC.Common.Caching.CacheNotifyAction;
|
||||
|
||||
namespace ASC.Core.Notify
|
||||
{
|
||||
[Scope]
|
||||
|
@ -50,7 +50,7 @@ namespace ASC.Core.Common.Notify
|
||||
|
||||
public void SendMessage(NotifyMessage m)
|
||||
{
|
||||
CacheMessage.Publish(m, CacheNotifyAction.Insert);
|
||||
CacheMessage.Publish(m, ASC.Common.Caching.CacheNotifyAction.Insert);
|
||||
}
|
||||
|
||||
public void RegisterUser(string userId, int tenantId, string token)
|
||||
@ -61,7 +61,7 @@ namespace ASC.Core.Common.Notify
|
||||
UserId = userId,
|
||||
TenantId = tenantId,
|
||||
Token = token
|
||||
}, CacheNotifyAction.Insert);
|
||||
}, ASC.Common.Caching.CacheNotifyAction.Insert);
|
||||
}
|
||||
|
||||
public void CreateOrUpdateClient(int tenantId, string token, int tokenLifespan, string proxy)
|
||||
@ -72,12 +72,12 @@ namespace ASC.Core.Common.Notify
|
||||
Token = token,
|
||||
TokenLifespan = tokenLifespan,
|
||||
Proxy = proxy
|
||||
}, CacheNotifyAction.Insert);
|
||||
}, ASC.Common.Caching.CacheNotifyAction.Insert);
|
||||
}
|
||||
|
||||
public void DisableClient(int tenantId)
|
||||
{
|
||||
CacheDisableClient.Publish(new DisableClientProto() { TenantId = tenantId }, CacheNotifyAction.Insert);
|
||||
CacheDisableClient.Publish(new DisableClientProto() { TenantId = tenantId }, ASC.Common.Caching.CacheNotifyAction.Insert);
|
||||
}
|
||||
|
||||
public string RegistrationToken(string userId, int tenantId)
|
||||
|
@ -65,7 +65,7 @@ namespace ASC.Data.Storage.Configuration
|
||||
{
|
||||
storageSettingsHelper.Clear(cdnSettings);
|
||||
}
|
||||
}, CacheNotifyAction.Remove);
|
||||
}, Common.Caching.CacheNotifyAction.Remove);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -168,7 +168,7 @@ namespace ASC.Data.Storage.Configuration
|
||||
var path = TenantPath.CreatePath(tenantId);
|
||||
foreach (var module in StorageFactoryConfig.GetModuleList("", true))
|
||||
{
|
||||
Cache.Publish(new DataStoreCacheItem() { TenantId = path, Module = module }, CacheNotifyAction.Remove);
|
||||
Cache.Publish(new DataStoreCacheItem() { TenantId = path, Module = module }, Common.Caching.CacheNotifyAction.Remove);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
using CacheNotifyAction = ASC.Common.Caching.CacheNotifyAction;
|
||||
|
||||
namespace ASC.Data.Storage.Encryption
|
||||
{
|
||||
[Scope]
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user