44 changed files with 720 additions and 559 deletions
@ -1,14 +0,0 @@ |
|||||
<Project Sdk="Microsoft.NET.Sdk"> |
|
||||
|
|
||||
<Import Project="..\..\..\common.props" /> |
|
||||
|
|
||||
<PropertyGroup> |
|
||||
<TargetFramework>netstandard2.0</TargetFramework> |
|
||||
<RootNamespace /> |
|
||||
</PropertyGroup> |
|
||||
|
|
||||
<ItemGroup> |
|
||||
<PackageReference Include="Volo.Abp.BackgroundJobs" Version="4.3.0" /> |
|
||||
</ItemGroup> |
|
||||
|
|
||||
</Project> |
|
||||
@ -1,43 +0,0 @@ |
|||||
namespace LINGYUN.Abp.BackgroundJobs |
|
||||
{ |
|
||||
public class RetryAsyncBackgroundJobArgs<TArgs> |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// 重试次数
|
|
||||
/// </summary>
|
|
||||
public int RetryCount { get; set; } = 0; |
|
||||
/// <summary>
|
|
||||
/// 重试间隔(毫秒)
|
|
||||
/// 默认 300000ms = 5min
|
|
||||
/// </summary>
|
|
||||
public double RetryIntervalMillisecond { get; set; } = 300000d; |
|
||||
/// <summary>
|
|
||||
/// 最大重试次数
|
|
||||
/// 默认 20
|
|
||||
/// </summary>
|
|
||||
public int MaxRetryCount { get; set; } = 20; |
|
||||
/// <summary>
|
|
||||
/// 作业参数
|
|
||||
/// </summary>
|
|
||||
public TArgs JobArgs { get; set; } |
|
||||
|
|
||||
public RetryAsyncBackgroundJobArgs() |
|
||||
{ |
|
||||
|
|
||||
} |
|
||||
|
|
||||
public RetryAsyncBackgroundJobArgs(TArgs jobArgs) |
|
||||
{ |
|
||||
JobArgs = jobArgs; |
|
||||
} |
|
||||
|
|
||||
public RetryAsyncBackgroundJobArgs(TArgs jobArgs, int retryCount = 0, double interval = 300000d, int maxRetryCount = 20) |
|
||||
{ |
|
||||
JobArgs = jobArgs; |
|
||||
|
|
||||
RetryCount = retryCount; |
|
||||
RetryIntervalMillisecond = interval; |
|
||||
MaxRetryCount = maxRetryCount; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,80 +0,0 @@ |
|||||
using Microsoft.Extensions.Logging; |
|
||||
using Microsoft.Extensions.Logging.Abstractions; |
|
||||
using System; |
|
||||
using System.Threading.Tasks; |
|
||||
using Volo.Abp.BackgroundJobs; |
|
||||
|
|
||||
namespace LINGYUN.Abp.BackgroundJobs |
|
||||
{ |
|
||||
public abstract class RetryAsyncBackgroundJobBase<TArgs> : IAsyncBackgroundJob<RetryAsyncBackgroundJobArgs<TArgs>> |
|
||||
{ |
|
||||
public ILogger<RetryAsyncBackgroundJobBase<TArgs>> Logger { get; set; } |
|
||||
|
|
||||
protected IBackgroundJobManager BackgroundJobManager { get; } |
|
||||
|
|
||||
protected RetryAsyncBackgroundJobBase( |
|
||||
IBackgroundJobManager backgroundJobManager) |
|
||||
{ |
|
||||
BackgroundJobManager = backgroundJobManager; |
|
||||
|
|
||||
Logger = NullLogger<RetryAsyncBackgroundJobBase<TArgs>>.Instance; |
|
||||
} |
|
||||
|
|
||||
public async Task ExecuteAsync(RetryAsyncBackgroundJobArgs<TArgs> args) |
|
||||
{ |
|
||||
if (args.RetryCount > args.MaxRetryCount) |
|
||||
{ |
|
||||
Logger.LogWarning("Job has failed and the maximum number of retries has been reached. The failure callback is about to enter"); |
|
||||
// 任务执行失败次数已达上限,调用用户定义回调,并不再执行
|
|
||||
await OnJobExecuteFailedAsync(args.JobArgs); |
|
||||
return; |
|
||||
} |
|
||||
try |
|
||||
{ |
|
||||
// 执行任务
|
|
||||
await ExecuteAsync(args.JobArgs, args.RetryCount); |
|
||||
// 执行完成后回调
|
|
||||
await OnJobExecuteCompletedAsync(args.JobArgs); |
|
||||
} |
|
||||
catch(Exception ex) |
|
||||
{ |
|
||||
Logger.LogWarning("Job execution has failed and a retry is imminent"); |
|
||||
Logger.LogWarning("Job running error:{0}", ex.Message); |
|
||||
|
|
||||
// 每次重试 间隔时间增加1.1倍
|
|
||||
var retryInterval = args.RetryIntervalMillisecond * 1.1; |
|
||||
var retryJobArgs = new RetryAsyncBackgroundJobArgs<TArgs>(args.JobArgs, |
|
||||
args.RetryCount + 1, retryInterval, args.MaxRetryCount); |
|
||||
|
|
||||
Logger.LogDebug("Job task is queued for the next execution"); |
|
||||
|
|
||||
// 计算优先级
|
|
||||
BackgroundJobPriority priority = BackgroundJobPriority.Normal; |
|
||||
|
|
||||
if (args.RetryCount <= (args.MaxRetryCount / 2) && |
|
||||
args.RetryCount > (args.MaxRetryCount / 3)) |
|
||||
{ |
|
||||
priority = BackgroundJobPriority.BelowNormal; |
|
||||
} |
|
||||
else if (args.RetryCount > (args.MaxRetryCount / 1.5)) |
|
||||
{ |
|
||||
priority = BackgroundJobPriority.Low; |
|
||||
} |
|
||||
// 延迟入队,等待下一次运行
|
|
||||
await BackgroundJobManager.EnqueueAsync(retryJobArgs, priority, delay: TimeSpan.FromMilliseconds(retryInterval)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
protected abstract Task ExecuteAsync(TArgs args, int retryCount); |
|
||||
|
|
||||
protected virtual Task OnJobExecuteFailedAsync(TArgs args) |
|
||||
{ |
|
||||
return Task.CompletedTask; |
|
||||
} |
|
||||
|
|
||||
protected virtual Task OnJobExecuteCompletedAsync(TArgs args) |
|
||||
{ |
|
||||
return Task.CompletedTask; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,15 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<Import Project="..\..\..\common.props" /> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>netstandard2.0</TargetFramework> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Volo.Abp.HangFire" Version="4.3.0" /> |
||||
|
<PackageReference Include="Volo.Abp.BackgroundWorkers" Version="4.3.0" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,19 @@ |
|||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Volo.Abp.BackgroundWorkers; |
||||
|
using Volo.Abp.Hangfire; |
||||
|
using Volo.Abp.Modularity; |
||||
|
|
||||
|
namespace LINGYUN.Abp.BackgroundWorkers.Hangfire |
||||
|
{ |
||||
|
[DependsOn( |
||||
|
typeof(AbpBackgroundWorkersModule), |
||||
|
typeof(AbpHangfireModule) |
||||
|
)] |
||||
|
public class AbpBackgroundWorkersHangfireModule : AbpModule |
||||
|
{ |
||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
context.Services.AddSingleton(typeof(HangfireBackgroundWorkerAdapter<>)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
using JetBrains.Annotations; |
||||
|
using System; |
||||
|
|
||||
|
namespace LINGYUN.Abp.BackgroundWorkers.Hangfire |
||||
|
{ |
||||
|
internal static class Check |
||||
|
{ |
||||
|
public static int Range( |
||||
|
int value, |
||||
|
[InvokerParameterName][NotNull] string parameterName, |
||||
|
int minimum = int.MinValue, |
||||
|
int maximum = int.MaxValue) |
||||
|
{ |
||||
|
if (value < minimum) |
||||
|
{ |
||||
|
throw new ArgumentException($"{parameterName} must be equal to or lower than {minimum}!", parameterName); |
||||
|
} |
||||
|
if (value > maximum) |
||||
|
{ |
||||
|
throw new ArgumentException($"{parameterName} must be equal to or greater than {maximum}!", parameterName); |
||||
|
} |
||||
|
return value; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,171 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace LINGYUN.Abp.BackgroundWorkers.Hangfire |
||||
|
{ |
||||
|
public class CronGenerator |
||||
|
{ |
||||
|
public const long MilliSecondsOfYear = 315_3600_0000L; |
||||
|
public const long MilliSecondsOfMonth = 26_7840_0000L; |
||||
|
public const int MilliSecondsOfWeek = 6_0480_0000; |
||||
|
public const int MilliSecondsOfDays = 8640_0000; |
||||
|
public const int MilliSecondsOfHours = 360_0000; |
||||
|
public const int MilliSecondsOfMinutes = 60000; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 从毫秒计算Cron表达式
|
||||
|
/// </summary>
|
||||
|
/// <param name="milliseconds"></param>
|
||||
|
/// <returns></returns>
|
||||
|
public static string FormMilliseconds(long milliseconds = 1000) |
||||
|
{ |
||||
|
if (milliseconds <= 1000) |
||||
|
{ |
||||
|
return Seconds(0, 1); |
||||
|
} |
||||
|
|
||||
|
if (milliseconds >= MilliSecondsOfYear) |
||||
|
{ |
||||
|
return Year(1, 1, 0, 0, 2001, (int)(milliseconds / MilliSecondsOfYear)); |
||||
|
} |
||||
|
|
||||
|
if (milliseconds >= MilliSecondsOfMonth) |
||||
|
{ |
||||
|
return Month(1, 0, 0, 1, (int)(milliseconds / MilliSecondsOfMonth)); |
||||
|
} |
||||
|
|
||||
|
// TODO: 以周为单位的任务Cron
|
||||
|
if (milliseconds >= MilliSecondsOfWeek) |
||||
|
{ |
||||
|
return Day(0, 0, 1, (int)(milliseconds / MilliSecondsOfWeek)); |
||||
|
} |
||||
|
|
||||
|
if (milliseconds >= MilliSecondsOfDays) |
||||
|
{ |
||||
|
return Day(0, 0, 1, (int)(milliseconds / MilliSecondsOfDays)); |
||||
|
} |
||||
|
|
||||
|
if (milliseconds >= MilliSecondsOfHours) |
||||
|
{ |
||||
|
return Hour(0, 0, (int)(milliseconds / MilliSecondsOfHours)); |
||||
|
} |
||||
|
|
||||
|
if (milliseconds >= MilliSecondsOfMinutes) |
||||
|
{ |
||||
|
return Minute(0, 0, (int)(milliseconds / MilliSecondsOfMinutes)); |
||||
|
} |
||||
|
|
||||
|
return Seconds(0, (int)(milliseconds / 1000)); |
||||
|
} |
||||
|
/// <summary>
|
||||
|
/// 周期性为秒钟的任务
|
||||
|
/// </summary>
|
||||
|
/// <param name="second">第几秒开始,默认为第0秒</param>
|
||||
|
/// <param name="interval">执行周期的间隔,默认为每5秒一次</param>
|
||||
|
/// <returns></returns>
|
||||
|
public static string Seconds(int second = 0, int interval = 5) |
||||
|
{ |
||||
|
Check.Range(second, nameof(second), 0, 59); |
||||
|
|
||||
|
return $"{second}/{interval} * * * * ? "; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 周期性为分钟的任务
|
||||
|
/// </summary>
|
||||
|
/// <param name="second">第几秒开始,默认为第0秒</param>
|
||||
|
/// <param name="minute">第几分钟开始,默认为第0分钟</param>
|
||||
|
/// <param name="interval">执行周期的间隔,默认为每分钟一次</param>
|
||||
|
/// <returns></returns>
|
||||
|
public static string Minute(int second = 0, int minute = 0, int interval = 1) |
||||
|
{ |
||||
|
Check.Range(second, nameof(second), 0, 59); |
||||
|
Check.Range(minute, nameof(minute), 0, 59); |
||||
|
|
||||
|
return $"{second} {minute}/{interval} * * * ? "; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 周期性为小时的任务
|
||||
|
/// </summary>
|
||||
|
/// <param name="minute">第几分钟开始,默认为第0分钟</param>
|
||||
|
/// <param name="hour">第几小时开始,默认为0点</param>
|
||||
|
/// <param name="interval">执行周期的间隔,默认为每小时一次</param>
|
||||
|
/// <returns></returns>
|
||||
|
public static string Hour(int minute = 0, int hour = 0, int interval = 1) |
||||
|
{ |
||||
|
Check.Range(hour, nameof(hour), 0, 23); |
||||
|
Check.Range(minute, nameof(minute), 0, 59); |
||||
|
|
||||
|
return $"0 {minute} {hour}/{interval} * * ? "; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 周期性为天的任务
|
||||
|
/// </summary>
|
||||
|
/// <param name="hour">第几小时开始,默认从0点开始</param>
|
||||
|
/// <param name="minute">第几分钟开始,默认从第0分钟开始</param>
|
||||
|
/// <param name="day">第几天开始,默认从第1天开始</param>
|
||||
|
/// <param name="interval">执行周期的间隔,默认为每天一次</param>
|
||||
|
/// <returns></returns>
|
||||
|
public static string Day(int hour = 0, int minute = 0, int day = 1, int interval = 1) |
||||
|
{ |
||||
|
Check.Range(hour, nameof(hour), 0, 23); |
||||
|
Check.Range(minute, nameof(minute), 0, 59); |
||||
|
Check.Range(day, nameof(day), 1, 31); |
||||
|
|
||||
|
return $"0 {minute} {hour} {day}/{interval} * ? "; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 周期性为周的任务
|
||||
|
/// </summary>
|
||||
|
/// <param name="dayOfWeek">星期几开始,默认从星期一点开始</param>
|
||||
|
/// <param name="hour">第几小时开始,默认从0点开始</param>
|
||||
|
/// <param name="minute">第几分钟开始,默认从第0分钟开始</param>
|
||||
|
/// <returns></returns>
|
||||
|
public static string Week(DayOfWeek dayOfWeek = DayOfWeek.Monday, int hour = 0, int minute = 0) |
||||
|
{ |
||||
|
Check.Range(hour, nameof(hour), 0, 23); |
||||
|
Check.Range(minute, nameof(minute), 0, 59); |
||||
|
|
||||
|
return $"{minute} {hour} * * {dayOfWeek.ToString().Substring(0, 3)}"; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 周期性为月的任务
|
||||
|
/// </summary>
|
||||
|
/// <param name="day">几号开始,默认从一号开始</param>
|
||||
|
/// <param name="hour">第几小时开始,默认从0点开始</param>
|
||||
|
/// <param name="minute">第几分钟开始,默认从第0分钟开始</param>
|
||||
|
/// <param name="month">第几月开始,默认从第1月开始</param>
|
||||
|
/// <param name="interval">月份间隔</param>
|
||||
|
/// <returns></returns>
|
||||
|
public static string Month(int day = 1, int hour = 0, int minute = 0, int month = 1, int interval = 1) |
||||
|
{ |
||||
|
Check.Range(hour, nameof(hour), 0, 23); |
||||
|
Check.Range(minute, nameof(minute), 0, 59); |
||||
|
Check.Range(day, nameof(day), 1, 31); |
||||
|
|
||||
|
return $"0 {minute} {hour} {day} {month}/{interval} ?"; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 周期性为年的任务
|
||||
|
/// </summary>
|
||||
|
/// <param name="month">几月开始,默认从一月开始</param>
|
||||
|
/// <param name="day">几号开始,默认从一号开始</param>
|
||||
|
/// <param name="hour">第几小时开始,默认从0点开始</param>
|
||||
|
/// <param name="year">第几年开始,默认从2001年开始</param>
|
||||
|
/// <param name="minute">第几分钟开始,默认从第0分钟开始</param>
|
||||
|
/// <returns></returns>
|
||||
|
public static string Year(int month = 1, int day = 1, int hour = 0, int minute = 0, int year = 2001, int interval = 1) |
||||
|
{ |
||||
|
Check.Range(hour, nameof(hour), 0, 23); |
||||
|
Check.Range(minute, nameof(minute), 0, 59); |
||||
|
Check.Range(day, nameof(day), 1, 31); |
||||
|
Check.Range(month, nameof(month), 1, 12); |
||||
|
|
||||
|
return $"0 {minute} {hour} {day} {month} ? {year}/{interval}"; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,47 @@ |
|||||
|
using System.Reflection; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.BackgroundWorkers; |
||||
|
|
||||
|
namespace LINGYUN.Abp.BackgroundWorkers.Hangfire |
||||
|
{ |
||||
|
public class HangfireBackgroundWorkerAdapter<TWorker> : BackgroundWorkerBase, IHangfireBackgroundWorkerAdapter |
||||
|
where TWorker : IBackgroundWorker |
||||
|
{ |
||||
|
private readonly MethodInfo _doWorkAsyncMethod; |
||||
|
private readonly MethodInfo _doWorkMethod; |
||||
|
|
||||
|
public HangfireBackgroundWorkerAdapter() |
||||
|
{ |
||||
|
_doWorkAsyncMethod = typeof(TWorker).GetMethod("DoWorkAsync", BindingFlags.Instance | BindingFlags.NonPublic); |
||||
|
_doWorkMethod = typeof(TWorker).GetMethod("DoWork", BindingFlags.Instance | BindingFlags.NonPublic); |
||||
|
} |
||||
|
|
||||
|
public virtual async Task ExecuteAsync() |
||||
|
{ |
||||
|
var worker = (IBackgroundWorker)ServiceProvider.GetService(typeof(TWorker)); |
||||
|
var workerContext = new PeriodicBackgroundWorkerContext(ServiceProvider); |
||||
|
|
||||
|
switch (worker) |
||||
|
{ |
||||
|
case AsyncPeriodicBackgroundWorkerBase asyncWorker: |
||||
|
{ |
||||
|
if (_doWorkAsyncMethod != null) |
||||
|
{ |
||||
|
await (Task)_doWorkAsyncMethod.Invoke(asyncWorker, new object[] { workerContext }); |
||||
|
} |
||||
|
|
||||
|
break; |
||||
|
} |
||||
|
case PeriodicBackgroundWorkerBase syncWorker: |
||||
|
{ |
||||
|
if (_doWorkMethod != null) |
||||
|
{ |
||||
|
_doWorkMethod.Invoke(syncWorker, new object[] { workerContext }); |
||||
|
} |
||||
|
|
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,63 @@ |
|||||
|
using Hangfire; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using System; |
||||
|
using System.Reflection; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.BackgroundWorkers; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace LINGYUN.Abp.BackgroundWorkers.Hangfire |
||||
|
{ |
||||
|
[Dependency(ReplaceServices = true)] |
||||
|
public class HangfireBackgroundWorkerManager : IBackgroundWorkerManager, ISingletonDependency |
||||
|
{ |
||||
|
protected IServiceProvider ServiceProvider { get; } |
||||
|
|
||||
|
public HangfireBackgroundWorkerManager( |
||||
|
IServiceProvider serviceProvider) |
||||
|
{ |
||||
|
ServiceProvider = serviceProvider; |
||||
|
} |
||||
|
|
||||
|
public void Add(IBackgroundWorker worker) |
||||
|
{ |
||||
|
var timer = worker.GetType() |
||||
|
.GetProperty("Timer", BindingFlags.NonPublic | BindingFlags.Instance)? |
||||
|
.GetValue(worker); |
||||
|
|
||||
|
if (timer == null) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
var period = timer.GetType() |
||||
|
.GetProperty("Period", BindingFlags.Public | BindingFlags.Instance)? |
||||
|
.GetValue(timer)? |
||||
|
.To<int>(); |
||||
|
|
||||
|
if (!period.HasValue) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
var adapterType = typeof(HangfireBackgroundWorkerAdapter<>).MakeGenericType(worker.GetType()); |
||||
|
var workerAdapter = ServiceProvider.GetRequiredService(adapterType) as IHangfireBackgroundWorkerAdapter; |
||||
|
|
||||
|
RecurringJob.AddOrUpdate( |
||||
|
recurringJobId: worker.GetType().FullName, |
||||
|
methodCall: () => workerAdapter.ExecuteAsync(), |
||||
|
cronExpression: CronGenerator.FormMilliseconds(period.Value)); |
||||
|
} |
||||
|
|
||||
|
public Task StartAsync(CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
|
||||
|
public Task StopAsync(CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,10 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.BackgroundWorkers; |
||||
|
|
||||
|
namespace LINGYUN.Abp.BackgroundWorkers.Hangfire |
||||
|
{ |
||||
|
public interface IHangfireBackgroundWorkerAdapter : IBackgroundWorker |
||||
|
{ |
||||
|
Task ExecuteAsync(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>netstandard2.0</TargetFramework> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<None Remove="LINGYUN\Abp\Hangfire\Dashboard\Localization\Resources\en.json" /> |
||||
|
<None Remove="LINGYUN\Abp\Hangfire\Dashboard\Localization\Resources\zh-Hans.json" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<EmbeddedResource Include="LINGYUN\Abp\Hangfire\Dashboard\Localization\Resources\en.json" /> |
||||
|
<EmbeddedResource Include="LINGYUN\Abp\Hangfire\Dashboard\Localization\Resources\zh-Hans.json" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Volo.Abp.Authorization" Version="4.3.0" /> |
||||
|
<PackageReference Include="Volo.Abp.Hangfire" Version="4.3.0" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,36 @@ |
|||||
|
using LINGYUN.Abp.Hangfire.Dashboard.Localization; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Volo.Abp.Authorization; |
||||
|
using Volo.Abp.Hangfire; |
||||
|
using Volo.Abp.Localization; |
||||
|
using Volo.Abp.Modularity; |
||||
|
using Volo.Abp.VirtualFileSystem; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Hangfire.Dashboard |
||||
|
{ |
||||
|
[DependsOn( |
||||
|
typeof(AbpLocalizationModule), |
||||
|
typeof(AbpAuthorizationModule), |
||||
|
typeof(AbpHangfireModule))] |
||||
|
public class AbpHangfireDashboardModule : AbpModule |
||||
|
{ |
||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
Configure<AbpVirtualFileSystemOptions>(options => |
||||
|
{ |
||||
|
options.FileSets.AddEmbedded<AbpHangfireDashboardModule>(); |
||||
|
}); |
||||
|
|
||||
|
Configure<AbpLocalizationOptions>(options => |
||||
|
{ |
||||
|
options.Resources.Add<HangfireDashboardResource>(); |
||||
|
}); |
||||
|
|
||||
|
context.Services.AddTransient(serviceProvider => |
||||
|
{ |
||||
|
var options = serviceProvider.GetRequiredService<AbpHangfireDashboardOptionsProvider>().Get(); |
||||
|
return context.Services.ExecutePreConfiguredActions(options); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,32 @@ |
|||||
|
using Hangfire; |
||||
|
using Hangfire.Dashboard; |
||||
|
using LINGYUN.Abp.Hangfire.Dashboard.Authorization; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Volo.Abp.Authorization.Permissions; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Threading; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Hangfire.Dashboard |
||||
|
{ |
||||
|
public class AbpHangfireDashboardOptionsProvider : ITransientDependency |
||||
|
{ |
||||
|
public virtual DashboardOptions Get() |
||||
|
{ |
||||
|
return new DashboardOptions |
||||
|
{ |
||||
|
Authorization = new IDashboardAuthorizationFilter[] |
||||
|
{ |
||||
|
new DashboardAuthorizationFilter() |
||||
|
}, |
||||
|
IsReadOnlyFunc = (context) => |
||||
|
{ |
||||
|
var httpContext = context.GetHttpContext(); |
||||
|
var permissionChecker = httpContext.RequestServices.GetRequiredService<IPermissionChecker>(); |
||||
|
|
||||
|
return !AsyncHelper.RunSync(async () => |
||||
|
await permissionChecker.IsGrantedAsync(HangfireDashboardPermissions.Dashboard.ManageJobs)); |
||||
|
} |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,32 @@ |
|||||
|
using Hangfire.Annotations; |
||||
|
using Hangfire.Dashboard; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using System.Linq; |
||||
|
using Volo.Abp.Authorization.Permissions; |
||||
|
using Volo.Abp.Threading; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Hangfire.Dashboard.Authorization |
||||
|
{ |
||||
|
public class DashboardAuthorizationFilter : IDashboardAuthorizationFilter |
||||
|
{ |
||||
|
internal readonly static string[] AllowRoutePrefixs = new string[] |
||||
|
{ |
||||
|
"/stats", |
||||
|
"/js", |
||||
|
"/css", |
||||
|
"/fonts" |
||||
|
}; |
||||
|
public bool Authorize([NotNull] DashboardContext context) |
||||
|
{ |
||||
|
if (AllowRoutePrefixs.Any(url => context.Request.Path.StartsWith(url))) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
var httpContext = context.GetHttpContext(); |
||||
|
var permissionChecker = httpContext.RequestServices.GetRequiredService<IPermissionChecker>(); |
||||
|
return AsyncHelper.RunSync(async () => |
||||
|
await permissionChecker.IsGrantedAsync(httpContext.User, HangfireDashboardPermissions.Dashboard.Default)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,33 @@ |
|||||
|
using LINGYUN.Abp.Hangfire.Dashboard.Localization; |
||||
|
using Volo.Abp.Authorization.Permissions; |
||||
|
using Volo.Abp.Localization; |
||||
|
using Volo.Abp.MultiTenancy; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Hangfire.Dashboard.Authorization |
||||
|
{ |
||||
|
public class HangfireDashboardPermissionDefinitionProvider : PermissionDefinitionProvider |
||||
|
{ |
||||
|
public override void Define(IPermissionDefinitionContext context) |
||||
|
{ |
||||
|
var group = context.AddGroup( |
||||
|
HangfireDashboardPermissions.GroupName, |
||||
|
L("Permission:Hangfire"), |
||||
|
MultiTenancySides.Host); // 除非对Hangfire Api进行改造,否则不能区分租户
|
||||
|
|
||||
|
var dashboard = group.AddPermission( |
||||
|
HangfireDashboardPermissions.Dashboard.Default, |
||||
|
L("Permission:Dashboard"), |
||||
|
MultiTenancySides.Host); |
||||
|
|
||||
|
dashboard.AddChild( |
||||
|
HangfireDashboardPermissions.Dashboard.ManageJobs, |
||||
|
L("Permission:ManageJobs"), |
||||
|
MultiTenancySides.Host); |
||||
|
} |
||||
|
|
||||
|
private static LocalizableString L(string name) |
||||
|
{ |
||||
|
return LocalizableString.Create<HangfireDashboardResource>(name); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
namespace LINGYUN.Abp.Hangfire.Dashboard.Authorization |
||||
|
{ |
||||
|
public static class HangfireDashboardPermissions |
||||
|
{ |
||||
|
public const string GroupName = "Hangfire"; |
||||
|
|
||||
|
public static class Dashboard |
||||
|
{ |
||||
|
public const string Default = GroupName + ".Dashboard"; |
||||
|
|
||||
|
public const string ManageJobs = Default + ".ManageJobs"; |
||||
|
// TODO: other pages...
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
using Volo.Abp.Localization; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Hangfire.Dashboard.Localization |
||||
|
{ |
||||
|
[LocalizationResourceName("HangfireDashboard")] |
||||
|
public class HangfireDashboardResource |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
{ |
||||
|
"culture": "en", |
||||
|
"texts": { |
||||
|
"Permission:Hangfire": "Hangfire", |
||||
|
"Permission:Dashboard": "Dashboard" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
{ |
||||
|
"culture": "zh-Hans", |
||||
|
"texts": { |
||||
|
"Permission:Hangfire": "Hangfire", |
||||
|
"Permission:Dashboard": "仪表板" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
using Microsoft.AspNetCore.Http; |
||||
|
|
||||
|
namespace Microsoft.AspNetCore.Builder |
||||
|
{ |
||||
|
public static class ApplicationBuilderAbpHangfireAuthoricationMiddlewareExtension |
||||
|
{ |
||||
|
public static IApplicationBuilder UseHangfireAuthorication(this IApplicationBuilder app) |
||||
|
{ |
||||
|
return app.UseMiddleware<HangfireAuthoricationMiddleware>(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,27 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace Microsoft.AspNetCore.Http |
||||
|
{ |
||||
|
public class HangfireAuthoricationMiddleware : IMiddleware, ITransientDependency |
||||
|
{ |
||||
|
public async Task InvokeAsync(HttpContext context, RequestDelegate next) |
||||
|
{ |
||||
|
// 通过 iframe 加载页面的话,需要手动传递 access_token 到参数列表
|
||||
|
if (context.Request.Path.StartsWithSegments("/hangfire") && |
||||
|
context.User.Identity?.IsAuthenticated != true) |
||||
|
{ |
||||
|
if (context.Request.Query.TryGetValue("access_token", out var accessTokens)) |
||||
|
{ |
||||
|
context.Request.Headers.Add("Authorization", accessTokens); |
||||
|
context.Response.Cookies.Append("access_token", accessTokens); |
||||
|
} |
||||
|
else if (context.Request.Cookies.TryGetValue("access_token", out string tokens)) |
||||
|
{ |
||||
|
context.Request.Headers.Add("Authorization", tokens); |
||||
|
} |
||||
|
} |
||||
|
await next(context); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,8 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Notifications.Workers |
||||
|
{ |
||||
|
public class Class1 |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,14 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<Import Project="..\..\..\common.props" /> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>netstandard2.0</TargetFramework> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\LINGYUN.Abp.Notifications\LINGYUN.Abp.Notifications.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,10 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Text; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Notifications.Workers.LINGYUN.Abp.Notifications.Workers |
||||
|
{ |
||||
|
class AbpNotificationsWorkersModule |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
@ -1,73 +0,0 @@ |
|||||
using Hangfire; |
|
||||
using Hangfire.Annotations; |
|
||||
using Hangfire.Dashboard; |
|
||||
using Microsoft.Extensions.DependencyInjection; |
|
||||
using Microsoft.Extensions.Options; |
|
||||
using NUglify.Helpers; |
|
||||
using System.Linq; |
|
||||
using Volo.Abp.Authorization.Permissions; |
|
||||
using Volo.Abp.Threading; |
|
||||
|
|
||||
namespace LINGYUN.Abp.MessageService.Authorization |
|
||||
{ |
|
||||
public class HangfireDashboardAuthorizationFilter : IDashboardAuthorizationFilter |
|
||||
{ |
|
||||
protected string[] AllowGrantPath { get; } |
|
||||
public HangfireDashboardAuthorizationFilter() |
|
||||
{ |
|
||||
AllowGrantPath = new string[] { "/css", "/js", "/fonts", "/stats" }; |
|
||||
} |
|
||||
|
|
||||
public bool Authorize([NotNull] DashboardContext context) |
|
||||
{ |
|
||||
// 放行路径
|
|
||||
if (AllowGrantPath.Contains(context.Request.Path)) |
|
||||
{ |
|
||||
return true; |
|
||||
} |
|
||||
var httpContext = context.GetHttpContext(); |
|
||||
|
|
||||
var options = httpContext.RequestServices.GetService<IOptions<HangfireDashboardRouteOptions>>()?.Value; |
|
||||
|
|
||||
if (options != null) |
|
||||
{ |
|
||||
// 白名单检查
|
|
||||
if (!context.Request.RemoteIpAddress.IsNullOrWhiteSpace() |
|
||||
&& options.IpAllow(context.Request.RemoteIpAddress)) |
|
||||
{ |
|
||||
return true; |
|
||||
} |
|
||||
// 请求路径对应的权限检查
|
|
||||
// TODO: 怎么来传递用户身份令牌?
|
|
||||
var permission = options.GetPermission(context.Request.Path); |
|
||||
if (!permission.IsNullOrWhiteSpace()) |
|
||||
{ |
|
||||
var permissionChecker = httpContext.RequestServices.GetRequiredService<IPermissionChecker>(); |
|
||||
return AsyncHelper.RunSync(async () => await permissionChecker.IsGrantedAsync(permission)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
public override int GetHashCode() |
|
||||
{ |
|
||||
// 类型相同就行了
|
|
||||
return GetType().FullName.GetHashCode(); |
|
||||
} |
|
||||
|
|
||||
public override bool Equals(object obj) |
|
||||
{ |
|
||||
if (obj == null) |
|
||||
{ |
|
||||
return false; |
|
||||
} |
|
||||
// 类型相同就行了
|
|
||||
if (GetType().Equals(obj.GetType())) |
|
||||
{ |
|
||||
return true; |
|
||||
} |
|
||||
return base.Equals(obj); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,38 +0,0 @@ |
|||||
using LINGYUN.Abp.Notifications; |
|
||||
using Microsoft.Extensions.Logging; |
|
||||
using System; |
|
||||
using System.Threading.Tasks; |
|
||||
using Volo.Abp.BackgroundJobs; |
|
||||
using Volo.Abp.DependencyInjection; |
|
||||
|
|
||||
namespace LINGYUN.Abp.MessageService.BackgroundJobs |
|
||||
{ |
|
||||
internal class NotificationCleanupExpritionJob : AsyncBackgroundJob<NotificationCleanupExpritionJobArgs>, ITransientDependency |
|
||||
{ |
|
||||
protected INotificationStore Store { get; } |
|
||||
protected IServiceProvider ServiceProvider { get; } |
|
||||
|
|
||||
public NotificationCleanupExpritionJob( |
|
||||
INotificationStore store, |
|
||||
IServiceProvider serviceProvider) |
|
||||
{ |
|
||||
Store = store; |
|
||||
ServiceProvider = serviceProvider; |
|
||||
} |
|
||||
|
|
||||
public override async Task ExecuteAsync(NotificationCleanupExpritionJobArgs args) |
|
||||
{ |
|
||||
try |
|
||||
{ |
|
||||
Logger.LogDebug("Before cleanup exprition jobs..."); |
|
||||
await Store.DeleteNotificationAsync(args.Count); |
|
||||
Logger.LogDebug("Exprition jobs cleanup job was successful..."); |
|
||||
} |
|
||||
catch (Exception ex) |
|
||||
{ |
|
||||
Logger.LogWarning("Exprition jobs cleanup job was failed..."); |
|
||||
Logger.LogWarning("Error:{0}", ex.Message); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,23 +0,0 @@ |
|||||
using Volo.Abp.BackgroundJobs; |
|
||||
|
|
||||
namespace LINGYUN.Abp.MessageService.BackgroundJobs |
|
||||
{ |
|
||||
[BackgroundJobName("定时清理过期通知消息任务")] |
|
||||
internal class NotificationCleanupExpritionJobArgs |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// 清理大小
|
|
||||
/// </summary>
|
|
||||
public int Count { get; set; } |
|
||||
|
|
||||
public NotificationCleanupExpritionJobArgs() |
|
||||
{ |
|
||||
|
|
||||
} |
|
||||
|
|
||||
public NotificationCleanupExpritionJobArgs(int count = 200) |
|
||||
{ |
|
||||
Count = count; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,74 +0,0 @@ |
|||||
using Hangfire.Dashboard; |
|
||||
using JetBrains.Annotations; |
|
||||
using System.Collections.Generic; |
|
||||
using Volo.Abp; |
|
||||
|
|
||||
namespace Hangfire |
|
||||
{ |
|
||||
public static class DashboardOptionsExtensions |
|
||||
{ |
|
||||
public static DashboardOptions AddAuthorization( |
|
||||
[NotNull] this DashboardOptions options, |
|
||||
[NotNull] IDashboardAuthorizationFilter authorizationFilter) |
|
||||
{ |
|
||||
Check.NotNull(options, nameof(options)); |
|
||||
Check.NotNull(authorizationFilter, nameof(authorizationFilter)); |
|
||||
|
|
||||
List<IDashboardAuthorizationFilter> filters = new List<IDashboardAuthorizationFilter>(); |
|
||||
filters.AddRange(options.Authorization); |
|
||||
filters.AddIfNotContains(authorizationFilter); |
|
||||
|
|
||||
options.Authorization = filters; |
|
||||
|
|
||||
return options; |
|
||||
} |
|
||||
|
|
||||
public static DashboardOptions AddAuthorizations( |
|
||||
[NotNull] this DashboardOptions options, |
|
||||
[NotNull] IEnumerable<IDashboardAuthorizationFilter> authorizationFilters) |
|
||||
{ |
|
||||
Check.NotNull(options, nameof(options)); |
|
||||
Check.NotNull(authorizationFilters, nameof(authorizationFilters)); |
|
||||
|
|
||||
List<IDashboardAuthorizationFilter> filters = new List<IDashboardAuthorizationFilter>(); |
|
||||
filters.AddRange(options.Authorization); |
|
||||
filters.AddIfNotContains(authorizationFilters); |
|
||||
|
|
||||
options.Authorization = filters; |
|
||||
|
|
||||
return options; |
|
||||
} |
|
||||
|
|
||||
public static DashboardOptions UseAuthorization( |
|
||||
[NotNull] this DashboardOptions options, |
|
||||
[NotNull] IDashboardAuthorizationFilter authorizationFilter) |
|
||||
{ |
|
||||
Check.NotNull(options, nameof(options)); |
|
||||
Check.NotNull(authorizationFilter, nameof(authorizationFilter)); |
|
||||
|
|
||||
List<IDashboardAuthorizationFilter> filters = new List<IDashboardAuthorizationFilter> |
|
||||
{ |
|
||||
authorizationFilter |
|
||||
}; |
|
||||
|
|
||||
options.Authorization = filters; |
|
||||
|
|
||||
return options; |
|
||||
} |
|
||||
|
|
||||
public static DashboardOptions UseAuthorizations( |
|
||||
[NotNull] this DashboardOptions options, |
|
||||
[NotNull] IEnumerable<IDashboardAuthorizationFilter> authorizationFilters) |
|
||||
{ |
|
||||
Check.NotNull(options, nameof(options)); |
|
||||
Check.NotNull(authorizationFilters, nameof(authorizationFilters)); |
|
||||
|
|
||||
List<IDashboardAuthorizationFilter> filters = new List<IDashboardAuthorizationFilter>(); |
|
||||
filters.AddRange(authorizationFilters); |
|
||||
|
|
||||
options.Authorization = filters; |
|
||||
|
|
||||
return options; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,47 +0,0 @@ |
|||||
using JetBrains.Annotations; |
|
||||
using Microsoft.AspNetCore.Builder; |
|
||||
using System; |
|
||||
using Volo.Abp; |
|
||||
|
|
||||
namespace Hangfire |
|
||||
{ |
|
||||
public static class HangfireApplicationBuilderExtensions |
|
||||
{ |
|
||||
public static IApplicationBuilder UseHangfireJwtToken( |
|
||||
[NotNull] this IApplicationBuilder app) |
|
||||
{ |
|
||||
return app.UseMiddleware<HangfireJwtTokenMiddleware>(); |
|
||||
} |
|
||||
|
|
||||
public static IApplicationBuilder UseHangfireDashboard( |
|
||||
[NotNull] this IApplicationBuilder app, |
|
||||
[CanBeNull] Action<DashboardOptions> setup = null) |
|
||||
{ |
|
||||
Check.NotNull(app, nameof(app)); |
|
||||
return app.UseHangfireDashboard("/hangfire", setup, null); |
|
||||
} |
|
||||
|
|
||||
public static IApplicationBuilder UseHangfireDashboard( |
|
||||
[NotNull] this IApplicationBuilder app, |
|
||||
[CanBeNull] string pathMatch = "/hangfire", |
|
||||
[CanBeNull] Action<DashboardOptions> setup = null) |
|
||||
{ |
|
||||
Check.NotNull(app, nameof(app)); |
|
||||
return app.UseHangfireDashboard(pathMatch, setup, null); |
|
||||
} |
|
||||
|
|
||||
public static IApplicationBuilder UseHangfireDashboard( |
|
||||
[NotNull] this IApplicationBuilder app, |
|
||||
[CanBeNull] string pathMatch = "/hangfire", |
|
||||
[CanBeNull] Action<DashboardOptions> setup = null, |
|
||||
[CanBeNull] JobStorage storage = null) |
|
||||
{ |
|
||||
Check.NotNull(app, nameof(app)); |
|
||||
|
|
||||
var options = new DashboardOptions(); |
|
||||
setup?.Invoke(options); |
|
||||
|
|
||||
return app.UseHangfireDashboard(pathMatch: pathMatch, options: options, storage: storage); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,74 +0,0 @@ |
|||||
using LINGYUN.Abp.MessageService.Permissions; |
|
||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Linq; |
|
||||
|
|
||||
namespace Hangfire |
|
||||
{ |
|
||||
public class HangfireDashboardRouteOptions |
|
||||
{ |
|
||||
public IList<string> AllowFrameOrigins { get; } |
|
||||
/// <summary>
|
|
||||
/// 白名单
|
|
||||
/// 添加网关地址
|
|
||||
/// </summary>
|
|
||||
public IList<string> WhiteList { get; } |
|
||||
public IDictionary<string, string> RoutePermissions { get; } |
|
||||
public HangfireDashboardRouteOptions() |
|
||||
{ |
|
||||
WhiteList = new List<string>(); |
|
||||
AllowFrameOrigins = new List<string>(); |
|
||||
RoutePermissions = new Dictionary<string, string>(); |
|
||||
InitDefaultRoutes(); |
|
||||
WithWhite("127.0.0.1"); |
|
||||
WithWhite("::1"); |
|
||||
} |
|
||||
|
|
||||
public bool IpAllow(string ipaddress) |
|
||||
{ |
|
||||
return WhiteList.Any(ip => ip == ipaddress); |
|
||||
} |
|
||||
|
|
||||
public void WithWhite(params string[] wgites) |
|
||||
{ |
|
||||
WhiteList.AddIfNotContains(wgites); |
|
||||
} |
|
||||
|
|
||||
public void WithOrigins(params string[] origins) |
|
||||
{ |
|
||||
AllowFrameOrigins.AddIfNotContains(origins); |
|
||||
} |
|
||||
|
|
||||
public void WithPermission(string route, string permission) |
|
||||
{ |
|
||||
RoutePermissions.Add(route, permission); |
|
||||
} |
|
||||
|
|
||||
public string GetPermission(string route) |
|
||||
{ |
|
||||
var permission = RoutePermissions |
|
||||
.Where(x => x.Key.StartsWith(route)) |
|
||||
.Select(x => x.Value) |
|
||||
.FirstOrDefault(); |
|
||||
|
|
||||
return permission; |
|
||||
} |
|
||||
|
|
||||
private void InitDefaultRoutes() |
|
||||
{ |
|
||||
WithPermission("/hangfire", MessageServicePermissions.Hangfire.Default); |
|
||||
WithPermission("/stats", MessageServicePermissions.Hangfire.Default); |
|
||||
WithPermission("/servers", MessageServicePermissions.Hangfire.Default); |
|
||||
WithPermission("/retries", MessageServicePermissions.Hangfire.Default); |
|
||||
WithPermission("/recurring", MessageServicePermissions.Hangfire.Default); |
|
||||
WithPermission("/jobs/enqueued", MessageServicePermissions.Hangfire.ManageQueue); |
|
||||
WithPermission("/jobs/processing", MessageServicePermissions.Hangfire.ManageQueue); |
|
||||
WithPermission("/jobs/scheduled", MessageServicePermissions.Hangfire.ManageQueue); |
|
||||
WithPermission("/jobs/failed", MessageServicePermissions.Hangfire.ManageQueue); |
|
||||
WithPermission("/jobs/deleted", MessageServicePermissions.Hangfire.ManageQueue); |
|
||||
WithPermission("/jobs/awaiting", MessageServicePermissions.Hangfire.ManageQueue); |
|
||||
WithPermission("/jobs/actions", MessageServicePermissions.Hangfire.ManageQueue); |
|
||||
WithPermission("/jobs/details", MessageServicePermissions.Hangfire.ManageQueue); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,31 +0,0 @@ |
|||||
using Microsoft.AspNetCore.Http; |
|
||||
using Microsoft.Extensions.DependencyInjection; |
|
||||
using Microsoft.Extensions.Options; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Threading.Tasks; |
|
||||
using Volo.Abp.DependencyInjection; |
|
||||
|
|
||||
namespace Hangfire |
|
||||
{ |
|
||||
public class HangfireJwtTokenMiddleware : IMiddleware, ITransientDependency |
|
||||
{ |
|
||||
public async Task InvokeAsync(HttpContext context, RequestDelegate next) |
|
||||
{ |
|
||||
// 通过 iframe 加载页面的话,需要手动传递 access_token 到参数列表
|
|
||||
if (context.Request.Path.StartsWithSegments("/hangfire") && context.User.Identity?.IsAuthenticated != true) |
|
||||
{ |
|
||||
if (context.Request.Query.TryGetValue("access_token", out var accessTokens)) |
|
||||
{ |
|
||||
context.Request.Headers.Add("Authorization", accessTokens); |
|
||||
} |
|
||||
var options = context.RequestServices.GetService<IOptions<HangfireDashboardRouteOptions>>()?.Value; |
|
||||
if (options != null && options.AllowFrameOrigins.Count > 0) |
|
||||
{ |
|
||||
// 跨域 iframe
|
|
||||
context.Response.Headers.TryAdd("X-Frame-Options", $"\"ALLOW-FROM {options.AllowFrameOrigins.JoinAsString(",")}\""); |
|
||||
} |
|
||||
} |
|
||||
await next(context); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
Loading…
Reference in new issue