@@ -49,7 +49,7 @@ namespace Microsoft.AspNetCore.Builder | |||
throw new InvalidOperationException("Add Cap must be called on the service collection."); | |||
} | |||
app.Map(new PathString(pathMatch), x => x.UseMiddleware<DashboardMiddleware>(storage, options, routes)); | |||
app.Map(new PathString(pathMatch), x => x.UseMiddleware<DashboardMiddleware>()); | |||
return app; | |||
} | |||
@@ -1,7 +1,5 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Net; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
using DotNetCore.CAP.Dashboard; | |||
using Microsoft.AspNetCore.Http; | |||
@@ -17,15 +15,10 @@ namespace DotNetCore.CAP | |||
public DashboardMiddleware(RequestDelegate next, DashboardOptions options, IStorage storage, RouteCollection routes) | |||
{ | |||
if (next == null) throw new ArgumentNullException(nameof(next)); | |||
if (storage == null) throw new ArgumentNullException(nameof(storage)); | |||
if (options == null) throw new ArgumentNullException(nameof(options)); | |||
if (routes == null) throw new ArgumentNullException(nameof(routes)); | |||
_next = next; | |||
_options = options; | |||
_storage = storage; | |||
_routes = routes; | |||
_next = next ?? throw new ArgumentNullException(nameof(next)); | |||
_options = options ?? throw new ArgumentNullException(nameof(options)); | |||
_storage = storage ?? throw new ArgumentNullException(nameof(storage)); | |||
_routes = routes ?? throw new ArgumentNullException(nameof(routes)); | |||
} | |||
public Task Invoke(HttpContext httpContext) | |||
@@ -38,7 +31,6 @@ namespace DotNetCore.CAP | |||
return _next.Invoke(httpContext); | |||
} | |||
// ReSharper disable once LoopCanBeConvertedToQuery | |||
foreach (var filter in _options.Authorization) | |||
{ | |||
if (!filter.Authorize(context)) | |||
@@ -1,10 +1,12 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Text; | |||
using Microsoft.Extensions.DependencyInjection; | |||
namespace DotNetCore.CAP | |||
{ | |||
using DotNetCore.CAP.Dashboard; | |||
using Microsoft.Extensions.DependencyInjection; | |||
internal sealed class DashboardOptionsExtension : ICapOptionsExtension | |||
{ | |||
private readonly Action<DashboardOptions> _options; | |||
@@ -19,17 +21,22 @@ namespace DotNetCore.CAP | |||
var dashboardOptions = new DashboardOptions(); | |||
_options?.Invoke(dashboardOptions); | |||
services.AddSingleton(dashboardOptions); | |||
services.AddSingleton(DashboardRoutes.Routes); | |||
} | |||
} | |||
} | |||
namespace Microsoft.Extensions.DependencyInjection | |||
{ | |||
using DotNetCore.CAP; | |||
public static class CapOptionsExtensions | |||
{ | |||
/// <summary> | |||
/// Configuration to use kafka in CAP. | |||
/// </summary> | |||
/// <param name="options">Provides programmatic configuration for the kafka .</param> | |||
/// <returns></returns> | |||
public static CapOptions UseDashboard(this CapOptions capOptions) | |||
{ | |||
return capOptions.UseDashboard(opt => {}); | |||
} | |||
public static CapOptions UseDashboard(this CapOptions capOptions, Action<DashboardOptions> options) | |||
{ | |||
if (options == null) throw new ArgumentNullException(nameof(options)); | |||
@@ -39,5 +46,4 @@ namespace DotNetCore.CAP | |||
return capOptions; | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,33 @@ | |||
using System; | |||
using System.Net; | |||
using System.Threading.Tasks; | |||
namespace DotNetCore.CAP.Dashboard | |||
{ | |||
internal class BatchCommandDispatcher : IDashboardDispatcher | |||
{ | |||
private readonly Action<DashboardContext, string> _command; | |||
public BatchCommandDispatcher(Action<DashboardContext, string> command) | |||
{ | |||
_command = command; | |||
} | |||
public async Task Dispatch(DashboardContext context) | |||
{ | |||
var jobIds = await context.Request.GetFormValuesAsync("jobs[]"); | |||
if (jobIds.Count == 0) | |||
{ | |||
context.Response.StatusCode = 422; | |||
return; | |||
} | |||
foreach (var jobId in jobIds) | |||
{ | |||
_command(context, jobId); | |||
} | |||
context.Response.StatusCode = (int)HttpStatusCode.NoContent; | |||
} | |||
} | |||
} |
@@ -0,0 +1,195 @@ | |||
using System.Reflection; | |||
using DotNetCore.CAP.Dashboard.Pages; | |||
using DotNetCore.CAP.Processor.States; | |||
namespace DotNetCore.CAP.Dashboard | |||
{ | |||
public static class DashboardRoutes | |||
{ | |||
private static readonly string[] Javascripts = | |||
{ | |||
"jquery-2.1.4.min.js", | |||
"bootstrap.min.js", | |||
"moment.min.js", | |||
"moment-with-locales.min.js", | |||
"d3.min.js", | |||
"d3.layout.min.js", | |||
"rickshaw.min.js", | |||
"cap.js" | |||
}; | |||
private static readonly string[] Stylesheets = | |||
{ | |||
"bootstrap.min.css", | |||
"rickshaw.min.css", | |||
"cap.css" | |||
}; | |||
static DashboardRoutes() | |||
{ | |||
Routes = new RouteCollection(); | |||
Routes.AddRazorPage("/", x => new HomePage()); | |||
Routes.Add("/stats", new JsonStats()); | |||
#region Embedded static content | |||
Routes.Add("/js[0-9]+", new CombinedResourceDispatcher( | |||
"application/javascript", | |||
GetExecutingAssembly(), | |||
GetContentFolderNamespace("js"), | |||
Javascripts)); | |||
Routes.Add("/css[0-9]+", new CombinedResourceDispatcher( | |||
"text/css", | |||
GetExecutingAssembly(), | |||
GetContentFolderNamespace("css"), | |||
Stylesheets)); | |||
Routes.Add("/fonts/glyphicons-halflings-regular/eot", new EmbeddedResourceDispatcher( | |||
"application/vnd.ms-fontobject", | |||
GetExecutingAssembly(), | |||
GetContentResourceName("fonts", "glyphicons-halflings-regular.eot"))); | |||
Routes.Add("/fonts/glyphicons-halflings-regular/svg", new EmbeddedResourceDispatcher( | |||
"image/svg+xml", | |||
GetExecutingAssembly(), | |||
GetContentResourceName("fonts", "glyphicons-halflings-regular.svg"))); | |||
Routes.Add("/fonts/glyphicons-halflings-regular/ttf", new EmbeddedResourceDispatcher( | |||
"application/octet-stream", | |||
GetExecutingAssembly(), | |||
GetContentResourceName("fonts", "glyphicons-halflings-regular.ttf"))); | |||
Routes.Add("/fonts/glyphicons-halflings-regular/woff", new EmbeddedResourceDispatcher( | |||
"font/woff", | |||
GetExecutingAssembly(), | |||
GetContentResourceName("fonts", "glyphicons-halflings-regular.woff"))); | |||
Routes.Add("/fonts/glyphicons-halflings-regular/woff2", new EmbeddedResourceDispatcher( | |||
"font/woff2", | |||
GetExecutingAssembly(), | |||
GetContentResourceName("fonts", "glyphicons-halflings-regular.woff2"))); | |||
#endregion | |||
#region Razor pages and commands | |||
//Routes.AddRazorPage("/jobs/enqueued", x => new QueuesPage()); | |||
//Routes.AddRazorPage( | |||
// "/jobs/enqueued/fetched/(?<Queue>.+)", | |||
// x => new FetchedJobsPage(x.Groups["Queue"].Value)); | |||
//Routes.AddClientBatchCommand("/jobs/enqueued/delete", (client, jobId) => client.ChangeState(jobId, CreateDeletedState())); | |||
//Routes.AddClientBatchCommand("/jobs/enqueued/requeue", (client, jobId) => client.ChangeState(jobId, CreateEnqueuedState())); | |||
//Routes.AddRazorPage( | |||
// "/jobs/enqueued/(?<Queue>.+)", | |||
// x => new EnqueuedJobsPage(x.Groups["Queue"].Value)); | |||
//Routes.AddRazorPage("/jobs/processing", x => new ProcessingJobsPage()); | |||
//Routes.AddClientBatchCommand( | |||
// "/jobs/processing/delete", | |||
// (client, jobId) => client.ChangeState(jobId, CreateDeletedState(), ProcessingState.StateName)); | |||
//Routes.AddClientBatchCommand( | |||
// "/jobs/processing/requeue", | |||
// (client, jobId) => client.ChangeState(jobId, CreateEnqueuedState(), ProcessingState.StateName)); | |||
//Routes.AddRazorPage("/jobs/scheduled", x => new ScheduledJobsPage()); | |||
//Routes.AddClientBatchCommand( | |||
// "/jobs/scheduled/enqueue", | |||
// (client, jobId) => client.ChangeState(jobId, CreateEnqueuedState(), ScheduledState.StateName)); | |||
//Routes.AddClientBatchCommand( | |||
// "/jobs/scheduled/delete", | |||
// (client, jobId) => client.ChangeState(jobId, CreateDeletedState(), ScheduledState.StateName)); | |||
//Routes.AddRazorPage("/jobs/succeeded", x => new SucceededJobs()); | |||
//Routes.AddClientBatchCommand( | |||
// "/jobs/succeeded/requeue", | |||
// (client, jobId) => client.ChangeState(jobId, CreateEnqueuedState(), SucceededState.StateName)); | |||
//Routes.AddRazorPage("/jobs/failed", x => new FailedJobsPage()); | |||
//Routes.AddClientBatchCommand( | |||
// "/jobs/failed/requeue", | |||
// (client, jobId) => client.ChangeState(jobId, CreateEnqueuedState(), FailedState.StateName)); | |||
//Routes.AddClientBatchCommand( | |||
// "/jobs/failed/delete", | |||
// (client, jobId) => client.ChangeState(jobId, CreateDeletedState(), FailedState.StateName)); | |||
//Routes.AddRazorPage("/jobs/deleted", x => new DeletedJobsPage()); | |||
//Routes.AddClientBatchCommand( | |||
// "/jobs/deleted/requeue", | |||
// (client, jobId) => client.ChangeState(jobId, CreateEnqueuedState(), DeletedState.StateName)); | |||
//Routes.AddRazorPage("/jobs/awaiting", x => new AwaitingJobsPage()); | |||
//Routes.AddClientBatchCommand("/jobs/awaiting/enqueue", (client, jobId) => client.ChangeState( | |||
// jobId, CreateEnqueuedState(), AwaitingState.StateName)); | |||
//Routes.AddClientBatchCommand("/jobs/awaiting/delete", (client, jobId) => client.ChangeState( | |||
// jobId, CreateDeletedState(), AwaitingState.StateName)); | |||
//Routes.AddCommand( | |||
// "/jobs/actions/requeue/(?<JobId>.+)", | |||
// context => | |||
// { | |||
// var client = new BackgroundJobClient(context.Storage); | |||
// return client.ChangeState(context.UriMatch.Groups["JobId"].Value, CreateEnqueuedState()); | |||
// }); | |||
//Routes.AddCommand( | |||
// "/jobs/actions/delete/(?<JobId>.+)", | |||
// context => | |||
// { | |||
// var client = new BackgroundJobClient(context.Storage); | |||
// return client.ChangeState(context.UriMatch.Groups["JobId"].Value, CreateDeletedState()); | |||
// }); | |||
//Routes.AddRazorPage("/jobs/details/(?<JobId>.+)", x => new JobDetailsPage(x.Groups["JobId"].Value)); | |||
//Routes.AddRazorPage("/recurring", x => new RecurringJobsPage()); | |||
//Routes.AddRecurringBatchCommand( | |||
// "/recurring/remove", | |||
// (manager, jobId) => manager.RemoveIfExists(jobId)); | |||
//Routes.AddRecurringBatchCommand( | |||
// "/recurring/trigger", | |||
// (manager, jobId) => manager.Trigger(jobId)); | |||
//Routes.AddRazorPage("/servers", x => new ServersPage()); | |||
//Routes.AddRazorPage("/retries", x => new RetriesPage()); | |||
#endregion | |||
} | |||
public static RouteCollection Routes { get; } | |||
internal static string GetContentFolderNamespace(string contentFolder) | |||
{ | |||
return $"{typeof (DashboardRoutes).Namespace}.Content.{contentFolder}"; | |||
} | |||
internal static string GetContentResourceName(string contentFolder, string resourceName) | |||
{ | |||
return $"{GetContentFolderNamespace(contentFolder)}.{resourceName}"; | |||
} | |||
//private static DeletedState CreateDeletedState() | |||
//{ | |||
// return new DeletedState { Reason = "Triggered via Dashboard UI" }; | |||
//} | |||
private static EnqueuedState CreateEnqueuedState() | |||
{ | |||
return new EnqueuedState();// { Reason = "Triggered via Dashboard UI" }; | |||
} | |||
private static Assembly GetExecutingAssembly() | |||
{ | |||
return typeof (DashboardRoutes).GetTypeInfo().Assembly; | |||
} | |||
} | |||
} |
@@ -20,7 +20,7 @@ namespace DotNetCore.CAP.Dashboard | |||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")] | |||
static JobHistoryRenderer() | |||
{ | |||
Register(SucceededState.StateName, SucceededRenderer); | |||
Register(SuccessedState.StateName, SucceededRenderer); | |||
Register(FailedState.StateName, FailedRenderer); | |||
Register(ProcessingState.StateName, ProcessingRenderer); | |||
Register(EnqueuedState.StateName, EnqueuedRenderer); | |||
@@ -29,7 +29,7 @@ namespace DotNetCore.CAP.Dashboard | |||
//Register(AwaitingState.StateName, AwaitingRenderer); | |||
BackgroundStateColors.Add(EnqueuedState.StateName, "#F5F5F5"); | |||
BackgroundStateColors.Add(SucceededState.StateName, "#EDF7ED"); | |||
BackgroundStateColors.Add(SuccessedState.StateName, "#EDF7ED"); | |||
BackgroundStateColors.Add(FailedState.StateName, "#FAEBEA"); | |||
BackgroundStateColors.Add(ProcessingState.StateName, "#FCEFDC"); | |||
BackgroundStateColors.Add(ScheduledState.StateName, "#E0F3F8"); | |||
@@ -37,7 +37,7 @@ namespace DotNetCore.CAP.Dashboard | |||
//BackgroundStateColors.Add(AwaitingState.StateName, "#F5F5F5"); | |||
ForegroundStateColors.Add(EnqueuedState.StateName, "#999"); | |||
ForegroundStateColors.Add(SucceededState.StateName, "#5cb85c"); | |||
ForegroundStateColors.Add(SuccessedState.StateName, "#5cb85c"); | |||
ForegroundStateColors.Add(FailedState.StateName, "#d9534f"); | |||
ForegroundStateColors.Add(ProcessingState.StateName, "#f0ad4e"); | |||
ForegroundStateColors.Add(ScheduledState.StateName, "#5bc0de"); | |||
@@ -9,7 +9,7 @@ | |||
<!DOCTYPE html> | |||
<html lang="@CultureInfo.CurrentUICulture.TwoLetterISOLanguageName"> | |||
<head> | |||
<title>@Title - Hangfire</title> | |||
<title>@Title - CAP</title> | |||
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |||
<meta charset="utf-8"> | |||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |||
@@ -29,7 +29,7 @@ | |||
<span class="icon-bar"></span> | |||
<span class="icon-bar"></span> | |||
</button> | |||
<a class="navbar-brand" href="@Url.Home()">Hangfire Dashboard</a> | |||
<a class="navbar-brand" href="@Url.Home()">CAP Dashboard</a> | |||
</div> | |||
<div class="collapse navbar-collapse"> | |||
@Html.RenderPartial(new Navigation()) | |||
@@ -58,7 +58,7 @@ | |||
<div class="container"> | |||
<ul class="list-inline credit"> | |||
<li> | |||
<a href="http://hangfire.io/" target="_blank">Hangfire @($"{version.Major}.{version.Minor}.{version.Build}") | |||
<a href="https://github.com/dotnetcore/cap/" target="_blank">Hangfire @($"{version.Major}.{version.Minor}.{version.Build}") | |||
</a> | |||
</li> | |||
<li>@Storage</li> | |||
@@ -68,7 +68,7 @@ | |||
</div> | |||
</div> | |||
<div id="hangfireConfig" | |||
<div id="capConfig" | |||
data-pollinterval="@StatsPollingInterval" | |||
data-pollurl="@(Url.To("/stats"))"> | |||
</div> | |||
@@ -89,7 +89,7 @@ WriteLiteral("\">\r\n<head>\r\n <title>"); | |||
#line default | |||
#line hidden | |||
WriteLiteral(" - Hangfire</title>\r\n <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\r\n " + | |||
WriteLiteral(" - CAP</title>\r\n <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\r\n " + | |||
" <meta charset=\"utf-8\">\r\n <meta name=\"viewport\" content=\"width=device-width" + | |||
", initial-scale=1.0\">\r\n"); | |||
@@ -136,7 +136,7 @@ WriteLiteral(@"""> | |||
#line default | |||
#line hidden | |||
WriteLiteral("\">Hangfire Dashboard</a>\r\n </div>\r\n <div cl" + | |||
WriteLiteral("\">CAP Dashboard</a>\r\n </div>\r\n <div cl" + | |||
"ass=\"collapse navbar-collapse\">\r\n "); | |||
@@ -211,7 +211,7 @@ WriteLiteral(@" | |||
<div class=""container""> | |||
<ul class=""list-inline credit""> | |||
<li> | |||
<a href=""http://hangfire.io/"" target=""_blank"">Hangfire "); | |||
<a href=""https://github.com/dotnetcore/cap/"" target=""_blank"">CAP "); | |||
@@ -0,0 +1,72 @@ | |||
using System; | |||
using System.Text.RegularExpressions; | |||
namespace DotNetCore.CAP.Dashboard | |||
{ | |||
public static class RouteCollectionExtensions | |||
{ | |||
public static void AddRazorPage( | |||
this RouteCollection routes, | |||
string pathTemplate, | |||
Func<Match, RazorPage> pageFunc) | |||
{ | |||
if (routes == null) throw new ArgumentNullException(nameof(routes)); | |||
if (pathTemplate == null) throw new ArgumentNullException(nameof(pathTemplate)); | |||
if (pageFunc == null) throw new ArgumentNullException(nameof(pageFunc)); | |||
routes.Add(pathTemplate, new RazorPageDispatcher(pageFunc)); | |||
} | |||
public static void AddCommand( | |||
this RouteCollection routes, | |||
string pathTemplate, | |||
Func<DashboardContext, bool> command) | |||
{ | |||
if (routes == null) throw new ArgumentNullException(nameof(routes)); | |||
if (pathTemplate == null) throw new ArgumentNullException(nameof(pathTemplate)); | |||
if (command == null) throw new ArgumentNullException(nameof(command)); | |||
routes.Add(pathTemplate, new CommandDispatcher(command)); | |||
} | |||
public static void AddBatchCommand( | |||
this RouteCollection routes, | |||
string pathTemplate, | |||
Action<DashboardContext, string> command) | |||
{ | |||
if (routes == null) throw new ArgumentNullException(nameof(routes)); | |||
if (pathTemplate == null) throw new ArgumentNullException(nameof(pathTemplate)); | |||
if (command == null) throw new ArgumentNullException(nameof(command)); | |||
routes.Add(pathTemplate, new BatchCommandDispatcher(command)); | |||
} | |||
//public static void AddClientBatchCommand( | |||
// this RouteCollection routes, | |||
// string pathTemplate, | |||
// [NotNull] Action<IBackgroundJobClient, string> command) | |||
//{ | |||
// if (command == null) throw new ArgumentNullException(nameof(command)); | |||
// routes.AddBatchCommand(pathTemplate, (context, jobId) => | |||
// { | |||
// var client = new BackgroundJobClient(context.Storage); | |||
// command(client, jobId); | |||
// }); | |||
//} | |||
//public static void AddRecurringBatchCommand( | |||
// this RouteCollection routes, | |||
// string pathTemplate, | |||
// Action<RecurringJobManager, string> command) | |||
//{ | |||
// if (command == null) throw new ArgumentNullException(nameof(command)); | |||
// routes.AddBatchCommand(pathTemplate, (context, jobId) => | |||
// { | |||
// var manager = new RecurringJobManager(context.Storage); | |||
// command(manager, jobId); | |||
// }); | |||
//} | |||
} | |||
} |
@@ -10,7 +10,6 @@ | |||
<ItemGroup> | |||
<None Remove="Dashboard\Content\css\bootstrap.min.css" /> | |||
<None Remove="Dashboard\Content\css\hangfire.css" /> | |||
<None Remove="Dashboard\Content\css\rickshaw.min.css" /> | |||
<None Remove="Dashboard\Content\fonts\glyphicons-halflings-regular.eot" /> | |||
<None Remove="Dashboard\Content\fonts\glyphicons-halflings-regular.svg" /> | |||
@@ -20,7 +19,6 @@ | |||
<None Remove="Dashboard\Content\js\bootstrap.min.js" /> | |||
<None Remove="Dashboard\Content\js\d3.layout.min.js" /> | |||
<None Remove="Dashboard\Content\js\d3.min.js" /> | |||
<None Remove="Dashboard\Content\js\hangfire.js" /> | |||
<None Remove="Dashboard\Content\js\jquery-2.1.4.min.js" /> | |||
<None Remove="Dashboard\Content\js\moment-with-locales.min.js" /> | |||
<None Remove="Dashboard\Content\js\moment.min.js" /> | |||
@@ -29,7 +27,7 @@ | |||
<ItemGroup> | |||
<EmbeddedResource Include="Dashboard\Content\css\bootstrap.min.css" /> | |||
<EmbeddedResource Include="Dashboard\Content\css\hangfire.css" /> | |||
<EmbeddedResource Include="Dashboard\Content\css\cap.css" /> | |||
<EmbeddedResource Include="Dashboard\Content\css\rickshaw.min.css" /> | |||
<EmbeddedResource Include="Dashboard\Content\fonts\glyphicons-halflings-regular.eot" /> | |||
<EmbeddedResource Include="Dashboard\Content\fonts\glyphicons-halflings-regular.svg" /> | |||
@@ -39,7 +37,7 @@ | |||
<EmbeddedResource Include="Dashboard\Content\js\bootstrap.min.js" /> | |||
<EmbeddedResource Include="Dashboard\Content\js\d3.layout.min.js" /> | |||
<EmbeddedResource Include="Dashboard\Content\js\d3.min.js" /> | |||
<EmbeddedResource Include="Dashboard\Content\js\hangfire.js" /> | |||
<EmbeddedResource Include="Dashboard\Content\js\cap.js" /> | |||
<EmbeddedResource Include="Dashboard\Content\js\jquery-2.1.4.min.js" /> | |||
<EmbeddedResource Include="Dashboard\Content\js\moment-with-locales.min.js" /> | |||
<EmbeddedResource Include="Dashboard\Content\js\moment.min.js" /> | |||