Merge pull request #537 from ONLYOFFICE/feature/asc-feed-aggreagator-refactor

Feature/asc feed aggreagator refactor
This commit is contained in:
Alexey Bannov 2022-02-18 13:50:46 +03:00 committed by GitHub
commit 91133d243e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 353 additions and 412 deletions

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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);
}
}
}

View File

@ -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);
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -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;

View File

@ -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) =>
{