diff --git a/Oqtane.Client/Modules/Admin/Jobs/Index.razor b/Oqtane.Client/Modules/Admin/Jobs/Index.razor index 1996d62b..2ff36730 100644 --- a/Oqtane.Client/Modules/Admin/Jobs/Index.razor +++ b/Oqtane.Client/Modules/Admin/Jobs/Index.razor @@ -36,13 +36,13 @@ else @context.NextExecution @if (context.IsStarted) - { + { - } - else - { + } + else + { - } + } @@ -100,11 +100,6 @@ else break; } - if (interval > 1) - { - result += Localizer["s"]; - } - return result; } @@ -114,6 +109,7 @@ else { await JobService.DeleteJobAsync(job.JobId); await logger.LogInformation("Job Deleted {Job}", job); + _jobs = await JobService.GetJobsAsync(); StateHasChanged(); } catch (Exception ex) @@ -125,12 +121,36 @@ else private async Task StartJob(int jobId) { - await JobService.StartJobAsync(jobId); + try + { + await JobService.StartJobAsync(jobId); + await logger.LogInformation("Job Started {JobId}", jobId); + AddModuleMessage(Localizer["Message.Job.Start"], MessageType.Success); + _jobs = await JobService.GetJobsAsync(); + StateHasChanged(); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Starting Job {JobId} {Error}", jobId, ex.Message); + AddModuleMessage(Localizer["Error.Job.Start"], MessageType.Error); + } } private async Task StopJob(int jobId) { - await JobService.StopJobAsync(jobId); + try + { + await JobService.StopJobAsync(jobId); + await logger.LogInformation("Job Stopped {JobId}", jobId); + AddModuleMessage(Localizer["Message.Job.Stop"], MessageType.Success); + _jobs = await JobService.GetJobsAsync(); + StateHasChanged(); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Stopping Job {JobId} {Error}", jobId, ex.Message); + AddModuleMessage(Localizer["Error.Job.Stop"], MessageType.Error); + } } private async Task Refresh() diff --git a/Oqtane.Client/Resources/Modules/Admin/Jobs/Index.resx b/Oqtane.Client/Resources/Modules/Admin/Jobs/Index.resx index a3da62e9..1ca4d846 100644 --- a/Oqtane.Client/Resources/Modules/Admin/Jobs/Index.resx +++ b/Oqtane.Client/Resources/Modules/Admin/Jobs/Index.resx @@ -144,9 +144,6 @@ Month - - s - Error Deleting Job @@ -177,4 +174,16 @@ Log + + An error occurred when starting the job + + + An error occurred when stopping the job + + + The process responsible for executing this job has been started. The next execution will be based on the schedule criteria for the job. + + + The process responsible for executing this job has been stopped. In order to restart the process you will need to use the Start button or restart the application. + \ No newline at end of file diff --git a/Oqtane.Server/Infrastructure/Jobs/HostedServiceBase.cs b/Oqtane.Server/Infrastructure/Jobs/HostedServiceBase.cs index 9e01968a..d208bc72 100644 --- a/Oqtane.Server/Infrastructure/Jobs/HostedServiceBase.cs +++ b/Oqtane.Server/Infrastructure/Jobs/HostedServiceBase.cs @@ -114,7 +114,7 @@ namespace Oqtane.Infrastructure jobLogs.UpdateJobLog(log); // update the job - job.NextExecution = CalculateNextExecution(NextExecution, job.Frequency, job.Interval); + job.NextExecution = CalculateNextExecution(NextExecution, job); job.IsExecuting = false; jobs.UpdateJob(job); @@ -140,21 +140,31 @@ namespace Oqtane.Infrastructure } - private DateTime CalculateNextExecution(DateTime nextExecution, string frequency, int interval) + private DateTime CalculateNextExecution(DateTime nextExecution, Job job) { - switch (frequency) + switch (job.Frequency) { case "m": // minutes - nextExecution = nextExecution.AddMinutes(interval); + nextExecution = nextExecution.AddMinutes(job.Interval); break; case "H": // hours - nextExecution = nextExecution.AddHours(interval); + nextExecution = nextExecution.AddHours(job.Interval); break; case "d": // days - nextExecution = nextExecution.AddDays(interval); + nextExecution = nextExecution.AddDays(job.Interval); + if (job.StartDate != null && job.StartDate.Value.TimeOfDay.TotalSeconds != 0) + { + // set the start time + nextExecution = nextExecution.Date.Add(job.StartDate.Value.TimeOfDay); + } break; case "M": // months - nextExecution = nextExecution.AddMonths(interval); + nextExecution = nextExecution.AddMonths(job.Interval); + if (job.StartDate != null && job.StartDate.Value.TimeOfDay.TotalSeconds != 0) + { + // set the start time + nextExecution = nextExecution.Date.Add(job.StartDate.Value.TimeOfDay); + } break; } if (nextExecution < DateTime.UtcNow) @@ -168,7 +178,6 @@ namespace Oqtane.Infrastructure { try { - // set IsExecuting to false in case this job was forcefully terminated previously using (var scope = _serviceScopeFactory.CreateScope()) { string jobTypeName = Utilities.GetFullTypeName(GetType().AssemblyQualifiedName); @@ -176,15 +185,17 @@ namespace Oqtane.Infrastructure Job job = jobs.GetJobs().Where(item => item.JobType == jobTypeName).FirstOrDefault(); if (job != null) { + // reset in case this job was forcefully terminated previously job.IsStarted = true; job.IsExecuting = false; jobs.UpdateJob(job); } else { - // auto registration - does not run on initial installation but will run after restart + // auto registration - job will not run on initial installation but will run after restart job = new Job { JobType = jobTypeName }; - // optional properties + + // optional HostedServiceBase properties var jobType = Type.GetType(jobTypeName); var jobObject = ActivatorUtilities.CreateInstance(scope.ServiceProvider, jobType) as HostedServiceBase; if (jobObject.Name != "") @@ -203,6 +214,7 @@ namespace Oqtane.Infrastructure job.IsEnabled = jobObject.IsEnabled; job.IsStarted = true; job.IsExecuting = false; + job.NextExecution = null; jobs.AddJob(job); } } @@ -224,13 +236,27 @@ namespace Oqtane.Infrastructure public async Task StopAsync(CancellationToken cancellationToken) { - if (_executingTask == null) - { - return; - } - try { + using (var scope = _serviceScopeFactory.CreateScope()) + { + string jobTypeName = Utilities.GetFullTypeName(GetType().AssemblyQualifiedName); + IJobRepository jobs = scope.ServiceProvider.GetRequiredService(); + Job job = jobs.GetJobs().Where(item => item.JobType == jobTypeName).FirstOrDefault(); + if (job != null) + { + // reset job + job.IsStarted = false; + job.IsExecuting = false; + jobs.UpdateJob(job); + } + } + + if (_executingTask == null) + { + return; + } + _cancellationTokenSource.Cancel(); } finally