Schedule Jobs from UI
This commit is contained in:
@@ -204,6 +204,79 @@ public class JobsController : Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UI-friendly endpoints (JWT auth)
|
||||||
|
[HttpPost]
|
||||||
|
[Route("ui/schedule")]
|
||||||
|
public async Task<IActionResult> ScheduleJobsUI([FromQuery] string? nameFilter = null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var jobsCreated = await _jobScheduler.ScheduleAllJobsAsync(nameFilter);
|
||||||
|
|
||||||
|
_logger.LogInformation("ScheduleJobsUI: Created {Count} jobs by user {UserId}", jobsCreated, User.Identity?.Name);
|
||||||
|
|
||||||
|
return Ok(new
|
||||||
|
{
|
||||||
|
success = true,
|
||||||
|
jobsCreated,
|
||||||
|
message = $"Successfully scheduled {jobsCreated} jobs"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "ScheduleJobsUI: Error scheduling jobs");
|
||||||
|
return BadRequest("An error occurred processing your request");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
[Route("ui/schedule/imports")]
|
||||||
|
public async Task<IActionResult> ScheduleImportJobsUI([FromQuery] string? nameFilter = null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var jobsCreated = await _jobScheduler.ScheduleImportJobsAsync(nameFilter);
|
||||||
|
|
||||||
|
_logger.LogInformation("ScheduleImportJobsUI: Created {Count} import jobs by user {UserId}", jobsCreated, User.Identity?.Name);
|
||||||
|
|
||||||
|
return Ok(new
|
||||||
|
{
|
||||||
|
success = true,
|
||||||
|
jobsCreated,
|
||||||
|
message = $"Successfully scheduled {jobsCreated} import jobs"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "ScheduleImportJobsUI: Error scheduling import jobs");
|
||||||
|
return BadRequest("An error occurred processing your request");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
[Route("ui/schedule/processes")]
|
||||||
|
public async Task<IActionResult> ScheduleProcessJobsUI()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var jobsCreated = await _jobScheduler.ScheduleProcessJobsAsync();
|
||||||
|
|
||||||
|
_logger.LogInformation("ScheduleProcessJobsUI: Created {Count} process jobs by user {UserId}", jobsCreated, User.Identity?.Name);
|
||||||
|
|
||||||
|
return Ok(new
|
||||||
|
{
|
||||||
|
success = true,
|
||||||
|
jobsCreated,
|
||||||
|
message = $"Successfully scheduled {jobsCreated} process jobs"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "ScheduleProcessJobsUI: Error scheduling process jobs");
|
||||||
|
return BadRequest("An error occurred processing your request");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[Route("{id:guid}/retry")]
|
[Route("{id:guid}/retry")]
|
||||||
public async Task<IActionResult> RetryJob(Guid id)
|
public async Task<IActionResult> RetryJob(Guid id)
|
||||||
|
|||||||
@@ -42,7 +42,33 @@
|
|||||||
</MudSelect>
|
</MudSelect>
|
||||||
</MudItem>
|
</MudItem>
|
||||||
|
|
||||||
<MudItem xs="12" sm="12" md="6" Class="d-flex justify-end align-center">
|
<MudItem xs="12" sm="12" md="6" Class="d-flex justify-end align-center gap-2">
|
||||||
|
<MudMenu Icon="@Icons.Material.Filled.PlayArrow"
|
||||||
|
Label="Schedule Jobs"
|
||||||
|
Variant="Variant.Filled"
|
||||||
|
Color="Color.Success"
|
||||||
|
Size="Size.Medium"
|
||||||
|
EndIcon="@Icons.Material.Filled.KeyboardArrowDown">
|
||||||
|
<MudMenuItem OnClick="@(() => ScheduleJobs("all"))">
|
||||||
|
<div class="d-flex align-center">
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.PlayCircle" Class="mr-2" />
|
||||||
|
<span>Run All Jobs</span>
|
||||||
|
</div>
|
||||||
|
</MudMenuItem>
|
||||||
|
<MudMenuItem OnClick="@(() => ScheduleJobs("imports"))">
|
||||||
|
<div class="d-flex align-center">
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.FileDownload" Class="mr-2" />
|
||||||
|
<span>Run All Imports</span>
|
||||||
|
</div>
|
||||||
|
</MudMenuItem>
|
||||||
|
<MudMenuItem OnClick="@(() => ScheduleJobs("processes"))">
|
||||||
|
<div class="d-flex align-center">
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.Settings" Class="mr-2" />
|
||||||
|
<span>Run All Processes</span>
|
||||||
|
</div>
|
||||||
|
</MudMenuItem>
|
||||||
|
</MudMenu>
|
||||||
|
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.Refresh"
|
<MudIconButton Icon="@Icons.Material.Filled.Refresh"
|
||||||
OnClick="LoadJobs"
|
OnClick="LoadJobs"
|
||||||
Color="Color.Primary"
|
Color="Color.Primary"
|
||||||
|
|||||||
@@ -129,6 +129,41 @@ public partial class Index : ComponentBase, IDisposable
|
|||||||
await JSRuntime.InvokeVoidAsync("open", url, "_blank");
|
await JSRuntime.InvokeVoidAsync("open", url, "_blank");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task ScheduleJobs(string type)
|
||||||
|
{
|
||||||
|
isLoading = true;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
(bool success, int jobsCreated, string message) result = type switch
|
||||||
|
{
|
||||||
|
"all" => await JobService.ScheduleAllJobsAsync(),
|
||||||
|
"imports" => await JobService.ScheduleImportJobsAsync(),
|
||||||
|
"processes" => await JobService.ScheduleProcessJobsAsync(),
|
||||||
|
_ => (false, 0, "Unknown job type")
|
||||||
|
};
|
||||||
|
|
||||||
|
if (result.success)
|
||||||
|
{
|
||||||
|
Snackbar.Add($"{result.message} ({result.jobsCreated} jobs created)", Severity.Success);
|
||||||
|
await LoadJobs();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Snackbar.Add(result.message, Severity.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Scheduling jobs failed: {ex.Message}");
|
||||||
|
Snackbar.Add($"Failed to schedule jobs: {ex.Message}", Severity.Error);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
isLoading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private Color GetStatusColor(JobStatus status)
|
private Color GetStatusColor(JobStatus status)
|
||||||
{
|
{
|
||||||
return status switch
|
return status switch
|
||||||
|
|||||||
@@ -88,6 +88,89 @@ public class JobService
|
|||||||
|
|
||||||
return await response.Content.ReadFromJsonAsync<CreateJobResult>();
|
return await response.Content.ReadFromJsonAsync<CreateJobResult>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<(bool success, int jobsCreated, string message)> ScheduleAllJobsAsync(string? nameFilter = null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var query = string.IsNullOrEmpty(nameFilter) ? "" : $"?nameFilter={Uri.EscapeDataString(nameFilter)}";
|
||||||
|
var response = await _httpClient.PostAsync($"Jobs/ui/schedule{query}", null);
|
||||||
|
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
var error = await response.Content.ReadAsStringAsync();
|
||||||
|
return (false, 0, $"Failed to schedule jobs: {error}");
|
||||||
|
}
|
||||||
|
|
||||||
|
var json = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonSerializer.Deserialize<JsonElement>(json, _jsonOptions);
|
||||||
|
|
||||||
|
var jobsCreated = result.GetProperty("jobsCreated").GetInt32();
|
||||||
|
var message = result.GetProperty("message").GetString() ?? "Jobs scheduled";
|
||||||
|
|
||||||
|
return (true, jobsCreated, message);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Scheduling jobs failed: {ex.Message}");
|
||||||
|
return (false, 0, $"Error: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<(bool success, int jobsCreated, string message)> ScheduleImportJobsAsync(string? nameFilter = null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var query = string.IsNullOrEmpty(nameFilter) ? "" : $"?nameFilter={Uri.EscapeDataString(nameFilter)}";
|
||||||
|
var response = await _httpClient.PostAsync($"Jobs/ui/schedule/imports{query}", null);
|
||||||
|
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
var error = await response.Content.ReadAsStringAsync();
|
||||||
|
return (false, 0, $"Failed to schedule import jobs: {error}");
|
||||||
|
}
|
||||||
|
|
||||||
|
var json = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonSerializer.Deserialize<JsonElement>(json, _jsonOptions);
|
||||||
|
|
||||||
|
var jobsCreated = result.GetProperty("jobsCreated").GetInt32();
|
||||||
|
var message = result.GetProperty("message").GetString() ?? "Import jobs scheduled";
|
||||||
|
|
||||||
|
return (true, jobsCreated, message);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Scheduling import jobs failed: {ex.Message}");
|
||||||
|
return (false, 0, $"Error: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<(bool success, int jobsCreated, string message)> ScheduleProcessJobsAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var response = await _httpClient.PostAsync("Jobs/ui/schedule/processes", null);
|
||||||
|
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
var error = await response.Content.ReadAsStringAsync();
|
||||||
|
return (false, 0, $"Failed to schedule process jobs: {error}");
|
||||||
|
}
|
||||||
|
|
||||||
|
var json = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonSerializer.Deserialize<JsonElement>(json, _jsonOptions);
|
||||||
|
|
||||||
|
var jobsCreated = result.GetProperty("jobsCreated").GetInt32();
|
||||||
|
var message = result.GetProperty("message").GetString() ?? "Process jobs scheduled";
|
||||||
|
|
||||||
|
return (true, jobsCreated, message);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Scheduling process jobs failed: {ex.Message}");
|
||||||
|
return (false, 0, $"Error: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class JobStats
|
public class JobStats
|
||||||
|
|||||||
Reference in New Issue
Block a user