Merge pull request #537 from ONLYOFFICE/feature/asc-feed-aggreagator-refactor
Feature/asc feed aggreagator refactor
This commit is contained in:
commit
91133d243e
@ -1,23 +1,36 @@
|
||||
namespace ASC.Feed.Configuration
|
||||
namespace ASC.Feed.Configuration;
|
||||
|
||||
[Singletone]
|
||||
public class FeedSettings
|
||||
{
|
||||
[Singletone]
|
||||
public class FeedSettings
|
||||
public string ServerRoot
|
||||
{
|
||||
private string serverRoot;
|
||||
public string ServerRoot { get => serverRoot ?? "http://*/"; set { serverRoot = value; } }
|
||||
get => _serverRoot ?? "http://*/";
|
||||
set => _serverRoot = value;
|
||||
}
|
||||
public TimeSpan AggregatePeriod
|
||||
{
|
||||
get => _aggregatePeriod == TimeSpan.Zero ? TimeSpan.FromMinutes(5) : _aggregatePeriod;
|
||||
set => _aggregatePeriod = value;
|
||||
}
|
||||
public TimeSpan AggregateInterval
|
||||
{
|
||||
get => _aggregateInterval == TimeSpan.Zero ? TimeSpan.FromDays(14) : _aggregateInterval;
|
||||
set => _aggregateInterval = value;
|
||||
}
|
||||
public TimeSpan RemovePeriod
|
||||
{
|
||||
get => _removePeriod == TimeSpan.Zero ? TimeSpan.FromDays(1) : _removePeriod;
|
||||
set => _removePeriod = value;
|
||||
}
|
||||
|
||||
private TimeSpan aggregatePeriod;
|
||||
public TimeSpan AggregatePeriod { get => aggregatePeriod == TimeSpan.Zero ? TimeSpan.FromMinutes(5) : aggregatePeriod; set { aggregatePeriod = value; } }
|
||||
private string _serverRoot;
|
||||
private TimeSpan _aggregatePeriod;
|
||||
private TimeSpan _aggregateInterval;
|
||||
private TimeSpan _removePeriod;
|
||||
|
||||
private TimeSpan aggregateInterval;
|
||||
public TimeSpan AggregateInterval { get => aggregateInterval == TimeSpan.Zero ? TimeSpan.FromDays(14) : aggregateInterval; set { aggregateInterval = value; } }
|
||||
|
||||
private TimeSpan removePeriod;
|
||||
public TimeSpan RemovePeriod { get => removePeriod == TimeSpan.Zero ? TimeSpan.FromDays(1) : removePeriod; set { removePeriod = value; } }
|
||||
|
||||
public FeedSettings(ConfigurationExtension configuration)
|
||||
{
|
||||
configuration.GetSetting("feed", this);
|
||||
}
|
||||
public FeedSettings(ConfigurationExtension configuration)
|
||||
{
|
||||
configuration.GetSetting("feed", this);
|
||||
}
|
||||
}
|
||||
|
@ -2,11 +2,23 @@
|
||||
global using System.Collections.Generic;
|
||||
global using System.Data;
|
||||
global using System.Linq;
|
||||
global using System.Threading;
|
||||
global using System.Threading.Tasks;
|
||||
|
||||
global using ASC.Common;
|
||||
global using ASC.Common.Caching;
|
||||
global using ASC.Common.Logging;
|
||||
global using ASC.Common.Utils;
|
||||
global using ASC.Core;
|
||||
global using ASC.Core.Common;
|
||||
global using ASC.Core.Notify.Signalr;
|
||||
global using ASC.Feed.Aggregator.Modules;
|
||||
global using ASC.Feed.Configuration;
|
||||
global using ASC.Feed.Data;
|
||||
global using ASC.Web.Core;
|
||||
|
||||
global using Autofac;
|
||||
|
||||
global using Microsoft.Extensions.DependencyInjection;
|
||||
global using Microsoft.Extensions.Hosting;
|
||||
global using Microsoft.Extensions.Options;
|
@ -2,9 +2,9 @@
|
||||
*
|
||||
* (c) Copyright Ascensio System Limited 2010-2020
|
||||
*
|
||||
* 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
|
||||
* 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
|
||||
@ -12,112 +12,106 @@
|
||||
*
|
||||
* 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
|
||||
* 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(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.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Feed.Aggregator.Modules
|
||||
namespace ASC.Feed.Aggregator.Modules;
|
||||
|
||||
public abstract class FeedModule : IFeedModule
|
||||
{
|
||||
public abstract class FeedModule : IFeedModule
|
||||
public abstract string Name { get; }
|
||||
public abstract string Product { get; }
|
||||
public abstract Guid ProductID { get; }
|
||||
protected abstract string DbId { get; }
|
||||
protected int Tenant => TenantManager.GetCurrentTenant().TenantId;
|
||||
|
||||
protected readonly TenantManager TenantManager;
|
||||
protected readonly WebItemSecurity WebItemSecurity;
|
||||
|
||||
protected FeedModule(TenantManager tenantManager, WebItemSecurity webItemSecurity)
|
||||
{
|
||||
public abstract string Name { get; }
|
||||
public abstract string Product { get; }
|
||||
public abstract Guid ProductID { get; }
|
||||
TenantManager = tenantManager;
|
||||
WebItemSecurity = webItemSecurity;
|
||||
}
|
||||
|
||||
protected abstract string DbId { get; }
|
||||
public abstract IEnumerable<Tuple<Feed, object>> GetFeeds(FeedFilter filter);
|
||||
|
||||
protected int Tenant
|
||||
public abstract IEnumerable<int> GetTenantsWithFeeds(DateTime fromTime);
|
||||
|
||||
public virtual void VisibleFor(List<Tuple<FeedRow, object>> feed, Guid userId)
|
||||
{
|
||||
if (!WebItemSecurity.IsAvailableForUser(ProductID, userId))
|
||||
{
|
||||
get { return TenantManager.GetCurrentTenant().TenantId; }
|
||||
return;
|
||||
}
|
||||
|
||||
protected TenantManager TenantManager { get; }
|
||||
protected WebItemSecurity WebItemSecurity { get; }
|
||||
|
||||
protected FeedModule(TenantManager tenantManager, WebItemSecurity webItemSecurity)
|
||||
foreach (var tuple in feed)
|
||||
{
|
||||
TenantManager = tenantManager;
|
||||
WebItemSecurity = webItemSecurity;
|
||||
if (VisibleFor(tuple.Item1.Feed, tuple.Item2, userId))
|
||||
{
|
||||
tuple.Item1.Users.Add(userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected string GetGroupId(string item, Guid author, string rootId = null, int action = -1)
|
||||
public virtual bool VisibleFor(Feed feed, object data, Guid userId)
|
||||
{
|
||||
return WebItemSecurity.IsAvailableForUser(ProductID, userId);
|
||||
}
|
||||
|
||||
protected static Guid ToGuid(object guid)
|
||||
{
|
||||
try
|
||||
{
|
||||
const int interval = 2;
|
||||
var str = guid as string;
|
||||
return !string.IsNullOrEmpty(str) ? new Guid(str) : Guid.Empty;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return Guid.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
var hours = now.Hour;
|
||||
var groupIdHours = hours - (hours % interval);
|
||||
protected string GetGroupId(string item, Guid author, string rootId = null, int action = -1)
|
||||
{
|
||||
const int interval = 2;
|
||||
|
||||
if (rootId == null)
|
||||
{
|
||||
// groupId = {item}_{author}_{date}
|
||||
return string.Format("{0}_{1}_{2}",
|
||||
item,
|
||||
author,
|
||||
now.ToString("yyyy.MM.dd.") + groupIdHours);
|
||||
}
|
||||
if (action == -1)
|
||||
{
|
||||
// groupId = {item}_{author}_{date}_{rootId}_{action}
|
||||
return string.Format("{0}_{1}_{2}_{3}",
|
||||
item,
|
||||
author,
|
||||
now.ToString("yyyy.MM.dd.") + groupIdHours,
|
||||
rootId);
|
||||
}
|
||||
var now = DateTime.UtcNow;
|
||||
var hours = now.Hour;
|
||||
var groupIdHours = hours - (hours % interval);
|
||||
|
||||
if (rootId == null)
|
||||
{
|
||||
// groupId = {item}_{author}_{date}
|
||||
return string.Format("{0}_{1}_{2}",
|
||||
item,
|
||||
author,
|
||||
now.ToString("yyyy.MM.dd.") + groupIdHours);
|
||||
}
|
||||
if (action == -1)
|
||||
{
|
||||
// groupId = {item}_{author}_{date}_{rootId}_{action}
|
||||
return string.Format("{0}_{1}_{2}_{3}_{4}",
|
||||
return string.Format("{0}_{1}_{2}_{3}",
|
||||
item,
|
||||
author,
|
||||
now.ToString("yyyy.MM.dd.") + groupIdHours,
|
||||
rootId,
|
||||
action);
|
||||
rootId);
|
||||
}
|
||||
|
||||
|
||||
public abstract IEnumerable<int> GetTenantsWithFeeds(DateTime fromTime);
|
||||
|
||||
public abstract IEnumerable<Tuple<Feed, object>> GetFeeds(FeedFilter filter);
|
||||
|
||||
public virtual bool VisibleFor(Feed feed, object data, Guid userId)
|
||||
{
|
||||
return WebItemSecurity.IsAvailableForUser(ProductID, userId);
|
||||
}
|
||||
|
||||
public virtual void VisibleFor(List<Tuple<FeedRow, object>> feed, Guid userId)
|
||||
{
|
||||
if (!WebItemSecurity.IsAvailableForUser(ProductID, userId)) return;
|
||||
|
||||
foreach (var tuple in feed)
|
||||
{
|
||||
if (VisibleFor(tuple.Item1.Feed, tuple.Item2, userId))
|
||||
{
|
||||
tuple.Item1.Users.Add(userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected static Guid ToGuid(object guid)
|
||||
{
|
||||
try
|
||||
{
|
||||
var str = guid as string;
|
||||
return !string.IsNullOrEmpty(str) ? new Guid(str) : Guid.Empty;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return Guid.Empty;
|
||||
}
|
||||
|
||||
}
|
||||
// groupId = {item}_{author}_{date}_{rootId}_{action}
|
||||
return string.Format("{0}_{1}_{2}_{3}_{4}",
|
||||
item,
|
||||
author,
|
||||
now.ToString("yyyy.MM.dd.") + groupIdHours,
|
||||
rootId,
|
||||
action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,20 +23,19 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.Feed.Aggregator.Modules
|
||||
namespace ASC.Feed.Aggregator.Modules;
|
||||
|
||||
public interface IFeedModule
|
||||
{
|
||||
public interface IFeedModule
|
||||
{
|
||||
string Name { get; }
|
||||
string Name { get; }
|
||||
|
||||
string Product { get; }
|
||||
string Product { get; }
|
||||
|
||||
IEnumerable<int> GetTenantsWithFeeds(DateTime fromTime);
|
||||
IEnumerable<int> GetTenantsWithFeeds(DateTime fromTime);
|
||||
|
||||
IEnumerable<Tuple<Feed, object>> GetFeeds(FeedFilter filter);
|
||||
IEnumerable<Tuple<Feed, object>> GetFeeds(FeedFilter filter);
|
||||
|
||||
bool VisibleFor(Feed feed, object data, Guid userId);
|
||||
bool VisibleFor(Feed feed, object data, Guid userId);
|
||||
|
||||
void VisibleFor(List<Tuple<FeedRow, object>> feed, Guid userId);
|
||||
}
|
||||
void VisibleFor(List<Tuple<FeedRow, object>> feed, Guid userId);
|
||||
}
|
||||
|
@ -1,338 +1,182 @@
|
||||
/*
|
||||
*
|
||||
* (c) Copyright Ascensio System Limited 2010-2020
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
namespace ASC.Feed.Aggregator.Service;
|
||||
|
||||
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using ASC.Common.Caching;
|
||||
using ASC.Common.Logging;
|
||||
using ASC.Core.Common;
|
||||
using ASC.Core.Notify.Signalr;
|
||||
using ASC.Feed.Aggregator.Modules;
|
||||
using ASC.Feed.Configuration;
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace ASC.Feed.Aggregator
|
||||
[Singletone]
|
||||
public class FeedAggregatorService : FeedBaseService
|
||||
{
|
||||
[Singletone(Additional = typeof(FeedAggregatorServiceExtension))]
|
||||
public class FeedAggregatorService : IHostedService, IDisposable
|
||||
protected override string LoggerName { get; set; } = "ASC.Feed.Aggregator";
|
||||
|
||||
private readonly SignalrServiceClient _signalrServiceClient;
|
||||
|
||||
public FeedAggregatorService(
|
||||
FeedSettings feedSettings,
|
||||
IServiceScopeFactory serviceScopeFactory,
|
||||
IOptionsMonitor<ILog> optionsMonitor,
|
||||
SignalrServiceClient signalrServiceClient)
|
||||
: base(feedSettings, serviceScopeFactory, optionsMonitor)
|
||||
{
|
||||
private ILog Log { get; set; }
|
||||
private SignalrServiceClient SignalrServiceClient { get; }
|
||||
_signalrServiceClient = signalrServiceClient;
|
||||
}
|
||||
|
||||
private Timer aggregateTimer;
|
||||
private Timer removeTimer;
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
Logger.Info("Feed Aggregator service running.");
|
||||
|
||||
private volatile bool isStopped;
|
||||
private readonly object aggregateLock = new object();
|
||||
private readonly object removeLock = new object();
|
||||
var cfg = FeedSettings;
|
||||
|
||||
private FeedSettings FeedSettings { get; }
|
||||
private IServiceProvider ServiceProvider { get; }
|
||||
|
||||
public FeedAggregatorService(
|
||||
FeedSettings feedSettings,
|
||||
IServiceProvider serviceProvider,
|
||||
IOptionsMonitor<ILog> optionsMonitor,
|
||||
IOptionsSnapshot<SignalrServiceClient> optionsSnapshot)
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
FeedSettings = feedSettings;
|
||||
ServiceProvider = serviceProvider;
|
||||
Log = optionsMonitor.Get("ASC.Feed.Agregator");
|
||||
SignalrServiceClient = optionsSnapshot.Get("counters");
|
||||
AggregateFeeds(cfg.AggregateInterval);
|
||||
|
||||
await Task.Delay(cfg.AggregatePeriod, stoppingToken);
|
||||
}
|
||||
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
Logger.Info("Feed Aggregator Service stopping.");
|
||||
}
|
||||
|
||||
private static T Attempt<T>(int count, Func<T> action)
|
||||
{
|
||||
var counter = 0;
|
||||
while (true)
|
||||
{
|
||||
var cfg = FeedSettings;
|
||||
isStopped = false;
|
||||
|
||||
aggregateTimer = new Timer(AggregateFeeds, cfg.AggregateInterval, TimeSpan.Zero, cfg.AggregatePeriod);
|
||||
removeTimer = new Timer(RemoveFeeds, cfg.AggregateInterval, cfg.RemovePeriod, cfg.RemovePeriod);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
isStopped = true;
|
||||
|
||||
if (aggregateTimer != null)
|
||||
{
|
||||
aggregateTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
aggregateTimer.Dispose();
|
||||
aggregateTimer = null;
|
||||
}
|
||||
|
||||
if (removeTimer != null)
|
||||
{
|
||||
removeTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
removeTimer.Dispose();
|
||||
removeTimer = null;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void AggregateFeeds(object interval)
|
||||
{
|
||||
if (!Monitor.TryEnter(aggregateLock)) return;
|
||||
|
||||
try
|
||||
{
|
||||
var cfg = FeedSettings;
|
||||
using var scope = ServiceProvider.CreateScope();
|
||||
var scopeClass = scope.ServiceProvider.GetService<FeedAggregatorServiceScope>();
|
||||
var cache = scope.ServiceProvider.GetService<ICache>();
|
||||
var (baseCommonLinkUtility, tenantManager, feedAggregateDataProvider, userManager, securityContext, authManager) = scopeClass;
|
||||
baseCommonLinkUtility.Initialize(cfg.ServerRoot);
|
||||
|
||||
var start = DateTime.UtcNow;
|
||||
Log.DebugFormat("Start of collecting feeds...");
|
||||
|
||||
var unreadUsers = new Dictionary<int, Dictionary<Guid, int>>();
|
||||
var modules = scope.ServiceProvider.GetService<IEnumerable<IFeedModule>>();
|
||||
|
||||
foreach (var module in modules)
|
||||
return action();
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (count < ++counter)
|
||||
{
|
||||
var result = new List<FeedRow>();
|
||||
var fromTime = feedAggregateDataProvider.GetLastTimeAggregate(module.GetType().Name);
|
||||
if (fromTime == default) fromTime = DateTime.UtcNow.Subtract((TimeSpan)interval);
|
||||
var toTime = DateTime.UtcNow;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var tenants = Attempt(10, () => module.GetTenantsWithFeeds(fromTime)).ToList();
|
||||
Log.DebugFormat("Find {1} tenants for module {0}.", module.GetType().Name, tenants.Count);
|
||||
private static bool TryAuthenticate(SecurityContext securityContext, AuthManager authManager, int tenantId, Guid userid)
|
||||
{
|
||||
try
|
||||
{
|
||||
securityContext.AuthenticateMeWithoutCookie(authManager.GetAccountByID(tenantId, userid));
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var tenant in tenants)
|
||||
private void AggregateFeeds(object interval)
|
||||
{
|
||||
try
|
||||
{
|
||||
var cfg = FeedSettings;
|
||||
using var scope = ServiceScopeFactory.CreateScope();
|
||||
var cache = scope.ServiceProvider.GetService<ICache>();
|
||||
var baseCommonLinkUtility = scope.ServiceProvider.GetService<BaseCommonLinkUtility>();
|
||||
baseCommonLinkUtility.Initialize(cfg.ServerRoot);
|
||||
|
||||
var start = DateTime.UtcNow;
|
||||
Logger.DebugFormat("Start of collecting feeds...");
|
||||
|
||||
var unreadUsers = new Dictionary<int, Dictionary<Guid, int>>();
|
||||
var modules = scope.ServiceProvider.GetService<IEnumerable<IFeedModule>>();
|
||||
|
||||
var feedAggregateDataProvider = scope.ServiceProvider.GetService<FeedAggregateDataProvider>();
|
||||
var tenantManager = scope.ServiceProvider.GetService<TenantManager>();
|
||||
var userManager = scope.ServiceProvider.GetService<UserManager>();
|
||||
var authManager = scope.ServiceProvider.GetService<AuthManager>();
|
||||
var securityContext = scope.ServiceProvider.GetService<SecurityContext>();
|
||||
|
||||
foreach (var module in modules)
|
||||
{
|
||||
var result = new List<FeedRow>();
|
||||
var fromTime = feedAggregateDataProvider.GetLastTimeAggregate(module.GetType().Name);
|
||||
if (fromTime == default) fromTime = DateTime.UtcNow.Subtract((TimeSpan)interval);
|
||||
var toTime = DateTime.UtcNow;
|
||||
|
||||
var tenants = Attempt(10, () => module.GetTenantsWithFeeds(fromTime)).ToList();
|
||||
Logger.DebugFormat("Find {1} tenants for module {0}.", module.GetType().Name, tenants.Count);
|
||||
|
||||
foreach (var tenant in tenants)
|
||||
{
|
||||
// Warning! There is hack here!
|
||||
// clearing the cache to get the correct acl
|
||||
cache.Remove("acl" + tenant);
|
||||
cache.Remove("/webitemsecurity/" + tenant);
|
||||
//cache.Remove(string.Format("sub/{0}/{1}/{2}", tenant, "6045b68c-2c2e-42db-9e53-c272e814c4ad", NotifyConstants.Event_NewCommentForMessage.ID));
|
||||
|
||||
try
|
||||
{
|
||||
// Warning! There is hack here!
|
||||
// clearing the cache to get the correct acl
|
||||
cache.Remove("acl" + tenant);
|
||||
cache.Remove("/webitemsecurity/" + tenant);
|
||||
//cache.Remove(string.Format("sub/{0}/{1}/{2}", tenant, "6045b68c-2c2e-42db-9e53-c272e814c4ad", NotifyConstants.Event_NewCommentForMessage.ID));
|
||||
|
||||
try
|
||||
if (tenantManager.GetTenant(tenant) == null)
|
||||
{
|
||||
if (tenantManager.GetTenant(tenant) == null)
|
||||
continue;
|
||||
}
|
||||
|
||||
tenantManager.SetCurrentTenant(tenant);
|
||||
var users = userManager.GetUsers();
|
||||
|
||||
var feeds = Attempt(10, () => module.GetFeeds(new FeedFilter(fromTime, toTime) { Tenant = tenant }).Where(r => r.Item1 != null).ToList());
|
||||
Logger.DebugFormat("{0} feeds in {1} tenant.", feeds.Count, tenant);
|
||||
|
||||
var tenant1 = tenant;
|
||||
var module1 = module;
|
||||
var feedsRow = feeds
|
||||
.Select(tuple => new Tuple<FeedRow, object>(new FeedRow(tuple.Item1)
|
||||
{
|
||||
Tenant = tenant1,
|
||||
ProductId = module1.Product
|
||||
}, tuple.Item2))
|
||||
.ToList();
|
||||
|
||||
foreach (var u in users)
|
||||
{
|
||||
if (!TryAuthenticate(securityContext, authManager, tenant1, u.ID))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
tenantManager.SetCurrentTenant(tenant);
|
||||
var users = userManager.GetUsers();
|
||||
|
||||
var feeds = Attempt(10, () => module.GetFeeds(new FeedFilter(fromTime, toTime) { Tenant = tenant }).Where(r => r.Item1 != null).ToList());
|
||||
Log.DebugFormat("{0} feeds in {1} tenant.", feeds.Count, tenant);
|
||||
|
||||
var tenant1 = tenant;
|
||||
var module1 = module;
|
||||
var feedsRow = feeds
|
||||
.Select(tuple => new Tuple<FeedRow, object>(new FeedRow(tuple.Item1)
|
||||
{
|
||||
Tenant = tenant1,
|
||||
ProductId = module1.Product
|
||||
}, tuple.Item2))
|
||||
.ToList();
|
||||
|
||||
foreach (var u in users)
|
||||
{
|
||||
if (isStopped)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!TryAuthenticate(securityContext, authManager, tenant1, u.ID))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
module.VisibleFor(feedsRow, u.ID);
|
||||
}
|
||||
|
||||
result.AddRange(feedsRow.Select(r => r.Item1));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.ErrorFormat("Tenant: {0}, {1}", tenant, ex);
|
||||
module.VisibleFor(feedsRow, u.ID);
|
||||
}
|
||||
|
||||
result.AddRange(feedsRow.Select(r => r.Item1));
|
||||
}
|
||||
|
||||
feedAggregateDataProvider.SaveFeeds(result, module.GetType().Name, toTime);
|
||||
|
||||
foreach (var res in result)
|
||||
catch (Exception ex)
|
||||
{
|
||||
foreach (var userGuid in res.Users.Where(userGuid => !userGuid.Equals(res.ModifiedById)))
|
||||
{
|
||||
if (!unreadUsers.TryGetValue(res.Tenant, out var dictionary))
|
||||
{
|
||||
dictionary = new Dictionary<Guid, int>();
|
||||
}
|
||||
if (dictionary.ContainsKey(userGuid))
|
||||
{
|
||||
++dictionary[userGuid];
|
||||
}
|
||||
else
|
||||
{
|
||||
dictionary.Add(userGuid, 1);
|
||||
}
|
||||
|
||||
unreadUsers[res.Tenant] = dictionary;
|
||||
}
|
||||
Logger.ErrorFormat("Tenant: {0}, {1}", tenant, ex);
|
||||
}
|
||||
}
|
||||
|
||||
SignalrServiceClient.SendUnreadUsers(unreadUsers);
|
||||
feedAggregateDataProvider.SaveFeeds(result, module.GetType().Name, toTime);
|
||||
|
||||
Log.DebugFormat("Time of collecting news: {0}", DateTime.UtcNow - start);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Monitor.Exit(aggregateLock);
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveFeeds(object interval)
|
||||
{
|
||||
if (!Monitor.TryEnter(removeLock)) return;
|
||||
|
||||
try
|
||||
{
|
||||
using var scope = ServiceProvider.CreateScope();
|
||||
var feedAggregateDataProvider = scope.ServiceProvider.GetService<FeedAggregateDataProvider>();
|
||||
Log.DebugFormat("Start of removing old news");
|
||||
feedAggregateDataProvider.RemoveFeedAggregate(DateTime.UtcNow.Subtract((TimeSpan)interval));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Monitor.Exit(removeLock);
|
||||
}
|
||||
}
|
||||
|
||||
private static T Attempt<T>(int count, Func<T> action)
|
||||
{
|
||||
var counter = 0;
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
foreach (var res in result)
|
||||
{
|
||||
return action();
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (count < ++counter)
|
||||
foreach (var userGuid in res.Users.Where(userGuid => !userGuid.Equals(res.ModifiedById)))
|
||||
{
|
||||
throw;
|
||||
if (!unreadUsers.TryGetValue(res.Tenant, out var dictionary))
|
||||
{
|
||||
dictionary = new Dictionary<Guid, int>();
|
||||
}
|
||||
if (dictionary.ContainsKey(userGuid))
|
||||
{
|
||||
++dictionary[userGuid];
|
||||
}
|
||||
else
|
||||
{
|
||||
dictionary.Add(userGuid, 1);
|
||||
}
|
||||
|
||||
unreadUsers[res.Tenant] = dictionary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_signalrServiceClient.SendUnreadUsers(unreadUsers);
|
||||
|
||||
Logger.DebugFormat("Time of collecting news: {0}", DateTime.UtcNow - start);
|
||||
}
|
||||
|
||||
private static bool TryAuthenticate(SecurityContext securityContext, AuthManager authManager, int tenantId, Guid userid)
|
||||
catch (Exception ex)
|
||||
{
|
||||
try
|
||||
{
|
||||
securityContext.AuthenticateMeWithoutCookie(authManager.GetAccountByID(tenantId, userid));
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (aggregateTimer != null)
|
||||
{
|
||||
aggregateTimer.Dispose();
|
||||
}
|
||||
|
||||
if (removeTimer != null)
|
||||
{
|
||||
removeTimer.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Scope]
|
||||
public class FeedAggregatorServiceScope
|
||||
{
|
||||
private BaseCommonLinkUtility BaseCommonLinkUtility { get; }
|
||||
private TenantManager TenantManager { get; }
|
||||
private FeedAggregateDataProvider FeedAggregateDataProvider { get; }
|
||||
private UserManager UserManager { get; }
|
||||
private SecurityContext SecurityContext { get; }
|
||||
private AuthManager AuthManager { get; }
|
||||
|
||||
public FeedAggregatorServiceScope(BaseCommonLinkUtility baseCommonLinkUtility,
|
||||
TenantManager tenantManager,
|
||||
FeedAggregateDataProvider feedAggregateDataProvider,
|
||||
UserManager userManager,
|
||||
SecurityContext securityContext,
|
||||
AuthManager authManager)
|
||||
{
|
||||
BaseCommonLinkUtility = baseCommonLinkUtility;
|
||||
TenantManager = tenantManager;
|
||||
FeedAggregateDataProvider = feedAggregateDataProvider;
|
||||
UserManager = userManager;
|
||||
SecurityContext = securityContext;
|
||||
AuthManager = authManager;
|
||||
}
|
||||
|
||||
public void Deconstruct(out BaseCommonLinkUtility baseCommonLinkUtility,
|
||||
out TenantManager tenantManager,
|
||||
out FeedAggregateDataProvider feedAggregateDataProvider,
|
||||
out UserManager userManager,
|
||||
out SecurityContext securityContext,
|
||||
out AuthManager authManager)
|
||||
{
|
||||
baseCommonLinkUtility = BaseCommonLinkUtility;
|
||||
tenantManager = TenantManager;
|
||||
feedAggregateDataProvider = FeedAggregateDataProvider;
|
||||
userManager = UserManager;
|
||||
securityContext = SecurityContext;
|
||||
authManager = AuthManager;
|
||||
}
|
||||
}
|
||||
|
||||
public static class FeedAggregatorServiceExtension
|
||||
{
|
||||
public static void Register(DIHelper services)
|
||||
{
|
||||
services.TryAdd<FeedAggregatorServiceScope>();
|
||||
Logger.Error(ex);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
namespace ASC.Feed.Aggregator.Service;
|
||||
|
||||
public abstract class FeedBaseService : BackgroundService
|
||||
{
|
||||
protected virtual string LoggerName { get; set; } = "ASC.Feed";
|
||||
|
||||
protected readonly ILog Logger;
|
||||
protected readonly FeedSettings FeedSettings;
|
||||
protected readonly IServiceScopeFactory ServiceScopeFactory;
|
||||
|
||||
public FeedBaseService(
|
||||
FeedSettings feedSettings,
|
||||
IServiceScopeFactory serviceScopeFactory,
|
||||
IOptionsMonitor<ILog> optionsMonitor)
|
||||
{
|
||||
FeedSettings = feedSettings;
|
||||
ServiceScopeFactory = serviceScopeFactory;
|
||||
Logger = optionsMonitor.Get(LoggerName);
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
namespace ASC.Feed.Aggregator.Service;
|
||||
|
||||
[Singletone]
|
||||
public class FeedCleanerService : FeedBaseService
|
||||
{
|
||||
protected override string LoggerName { get; set; } = "ASC.Feed.Cleaner";
|
||||
|
||||
public FeedCleanerService(
|
||||
FeedSettings feedSettings,
|
||||
IServiceScopeFactory serviceScopeFactory,
|
||||
IOptionsMonitor<ILog> optionsMonitor)
|
||||
: base(feedSettings, serviceScopeFactory, optionsMonitor)
|
||||
{
|
||||
}
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
Logger.Info("Feed Cleaner Service running.");
|
||||
|
||||
var cfg = FeedSettings;
|
||||
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
await Task.Delay(cfg.RemovePeriod, stoppingToken);
|
||||
|
||||
RemoveFeeds(cfg.AggregateInterval);
|
||||
}
|
||||
|
||||
Logger.Info("Feed Cleaner Service stopping.");
|
||||
}
|
||||
|
||||
private void RemoveFeeds(object interval)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var scope = ServiceScopeFactory.CreateScope();
|
||||
var feedAggregateDataProvider = scope.ServiceProvider.GetService<FeedAggregateDataProvider>();
|
||||
|
||||
Logger.DebugFormat("Start of removing old news");
|
||||
|
||||
feedAggregateDataProvider.RemoveFeedAggregate(DateTime.UtcNow.Subtract((TimeSpan)interval));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error(ex);
|
||||
}
|
||||
}
|
||||
}
|
@ -34,6 +34,7 @@ global using ASC.Web.Files;
|
||||
global using ASC.Web.Files.Classes;
|
||||
global using ASC.Web.Files.Core;
|
||||
global using ASC.Web.Files.Core.Search;
|
||||
global using ASC.Feed.Aggregator.Service;
|
||||
global using ASC.Web.Files.Services.DocumentService;
|
||||
|
||||
global using Autofac;
|
||||
|
@ -93,9 +93,19 @@ namespace ASC.Files.Service
|
||||
services.AddHostedService<FeedAggregatorService>();
|
||||
diHelper.TryAdd<FeedAggregatorService>();
|
||||
|
||||
services.AddHostedService<FeedCleanerService>();
|
||||
diHelper.TryAdd<FeedCleanerService>();
|
||||
|
||||
services.AddHostedService<Launcher>();
|
||||
diHelper.TryAdd<Launcher>();
|
||||
|
||||
diHelper.TryAdd<AuthManager>();
|
||||
diHelper.TryAdd<BaseCommonLinkUtility>();
|
||||
diHelper.TryAdd<FeedAggregateDataProvider>();
|
||||
diHelper.TryAdd<SecurityContext>();
|
||||
diHelper.TryAdd<TenantManager>();
|
||||
diHelper.TryAdd<UserManager>();
|
||||
|
||||
})
|
||||
.ConfigureContainer<ContainerBuilder>((context, builder) =>
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user