diff --git a/src/DotNetCore.CAP/CAP.AppBuilderExtensions.cs b/src/DotNetCore.CAP/CAP.AppBuilderExtensions.cs index e21044f..eec7c76 100644 --- a/src/DotNetCore.CAP/CAP.AppBuilderExtensions.cs +++ b/src/DotNetCore.CAP/CAP.AppBuilderExtensions.cs @@ -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(storage, options, routes)); + app.Map(new PathString(pathMatch), x => x.UseMiddleware()); return app; } diff --git a/src/DotNetCore.CAP/CAP.DashboardMiddleware.cs b/src/DotNetCore.CAP/CAP.DashboardMiddleware.cs index 47716bf..82a34f1 100644 --- a/src/DotNetCore.CAP/CAP.DashboardMiddleware.cs +++ b/src/DotNetCore.CAP/CAP.DashboardMiddleware.cs @@ -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)) diff --git a/src/DotNetCore.CAP/CAP.DashboardOptionsExtensions.cs b/src/DotNetCore.CAP/CAP.DashboardOptionsExtensions.cs index 9080771..4e545e0 100644 --- a/src/DotNetCore.CAP/CAP.DashboardOptionsExtensions.cs +++ b/src/DotNetCore.CAP/CAP.DashboardOptionsExtensions.cs @@ -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 _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 { - /// - /// Configuration to use kafka in CAP. - /// - /// Provides programmatic configuration for the kafka . - /// + public static CapOptions UseDashboard(this CapOptions capOptions) + { + return capOptions.UseDashboard(opt => {}); + } + public static CapOptions UseDashboard(this CapOptions capOptions, Action options) { if (options == null) throw new ArgumentNullException(nameof(options)); @@ -39,5 +46,4 @@ namespace DotNetCore.CAP return capOptions; } } - -} +} \ No newline at end of file diff --git a/src/DotNetCore.CAP/Dashboard/BatchCommandDispatcher.cs b/src/DotNetCore.CAP/Dashboard/BatchCommandDispatcher.cs new file mode 100644 index 0000000..5b76389 --- /dev/null +++ b/src/DotNetCore.CAP/Dashboard/BatchCommandDispatcher.cs @@ -0,0 +1,33 @@ +using System; +using System.Net; +using System.Threading.Tasks; + +namespace DotNetCore.CAP.Dashboard +{ + internal class BatchCommandDispatcher : IDashboardDispatcher + { + private readonly Action _command; + + public BatchCommandDispatcher(Action 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; + } + } +} diff --git a/src/DotNetCore.CAP/Dashboard/DashboardRoutes.cs b/src/DotNetCore.CAP/Dashboard/DashboardRoutes.cs new file mode 100644 index 0000000..7ab21d9 --- /dev/null +++ b/src/DotNetCore.CAP/Dashboard/DashboardRoutes.cs @@ -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/(?.+)", + // 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/(?.+)", + // 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/(?.+)", + // context => + // { + // var client = new BackgroundJobClient(context.Storage); + // return client.ChangeState(context.UriMatch.Groups["JobId"].Value, CreateEnqueuedState()); + // }); + + //Routes.AddCommand( + // "/jobs/actions/delete/(?.+)", + // context => + // { + // var client = new BackgroundJobClient(context.Storage); + // return client.ChangeState(context.UriMatch.Groups["JobId"].Value, CreateDeletedState()); + // }); + + //Routes.AddRazorPage("/jobs/details/(?.+)", 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; + } + } +} diff --git a/src/DotNetCore.CAP/Dashboard/JobHistoryRenderer.cs b/src/DotNetCore.CAP/Dashboard/JobHistoryRenderer.cs index 04d0d0e..5df9a94 100644 --- a/src/DotNetCore.CAP/Dashboard/JobHistoryRenderer.cs +++ b/src/DotNetCore.CAP/Dashboard/JobHistoryRenderer.cs @@ -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"); diff --git a/src/DotNetCore.CAP/Dashboard/Pages/LayoutPage.cshtml b/src/DotNetCore.CAP/Dashboard/Pages/LayoutPage.cshtml index 3d927df..e07fff2 100644 --- a/src/DotNetCore.CAP/Dashboard/Pages/LayoutPage.cshtml +++ b/src/DotNetCore.CAP/Dashboard/Pages/LayoutPage.cshtml @@ -9,7 +9,7 @@ - @Title - Hangfire + @Title - CAP @@ -29,7 +29,7 @@ - Hangfire Dashboard + CAP Dashboard -
diff --git a/src/DotNetCore.CAP/Dashboard/Pages/LayoutPage.generated.cs b/src/DotNetCore.CAP/Dashboard/Pages/LayoutPage.generated.cs index 1bc5b6a..2db5e0d 100644 --- a/src/DotNetCore.CAP/Dashboard/Pages/LayoutPage.generated.cs +++ b/src/DotNetCore.CAP/Dashboard/Pages/LayoutPage.generated.cs @@ -89,7 +89,7 @@ WriteLiteral("\">\r\n\r\n "); #line default #line hidden -WriteLiteral(" - Hangfire\r\n \r\n " + +WriteLiteral(" - CAP\r\n \r\n " + " \r\n \r\n"); @@ -136,7 +136,7 @@ WriteLiteral(@"""> #line default #line hidden -WriteLiteral("\">Hangfire Dashboard\r\n \r\n
CAP Dashboard\r\n
\r\n
\r\n "); @@ -211,7 +211,7 @@ WriteLiteral(@"