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