155 lines
6.8 KiB
C#
155 lines
6.8 KiB
C#
using Butter.Dtos;
|
|
using Butter.Dtos.Jobs;
|
|
using Butter.Types;
|
|
using Lactose.Jobs;
|
|
using Lactose.Services;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
|
|
namespace Lactose.Controllers;
|
|
|
|
/// <summary>
|
|
/// Manages job lifecycle — listing active/past jobs, cancelling, and enqueuing new jobs.
|
|
/// </summary>
|
|
/// <param name="jobManager">The job manager for job lifecycle.</param>
|
|
/// <param name="jobScheduler">The job scheduler for queueing jobs.</param>
|
|
/// <param name="authService">The authentication service.</param>
|
|
[ApiController]
|
|
[Authorize(Roles = "Admin")]
|
|
[Route("api/[controller]")]
|
|
public class JobsController(JobManager jobManager, JobScheduler jobScheduler, LactoseAuthService authService) : ControllerBase {
|
|
|
|
/// <summary>
|
|
/// Returns active root jobs only — no children, no past jobs. Lightweight, polled frequently.
|
|
/// </summary>
|
|
[HttpGet]
|
|
[Authorize(Roles = "Admin")]
|
|
public ActionResult<List<JobStatusDto>> GetActiveRoots() {
|
|
var accessLevel = authService.GetUserData(User)?.AccessLevel ?? EAccessLevel.User;
|
|
if (accessLevel != EAccessLevel.Admin) { return Unauthorized(); }
|
|
return Ok(jobManager.GetActiveRootJobs());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a page of past root jobs with total count. Polled less frequently or on demand.
|
|
/// </summary>
|
|
[HttpGet("past")]
|
|
[Authorize(Roles = "Admin")]
|
|
public ActionResult<PastJobsResponse> GetPast([FromQuery] int page = 0, [FromQuery] int pageSize = 5) {
|
|
if (page < 0 || pageSize < 1 || pageSize > PagedParametersDto.MaxPageSize) { return BadRequest(); }
|
|
var accessLevel = authService.GetUserData(User)?.AccessLevel ?? EAccessLevel.User;
|
|
if (accessLevel != EAccessLevel.Admin) { return Unauthorized(); }
|
|
var (jobs, total) = jobManager.GetPastRootJobs(page, pageSize);
|
|
return Ok(new PastJobsResponse { Jobs = jobs, Total = total });
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns children of a specific parent job (active + past).
|
|
/// Supports delta sync — pass <paramref name="since"/> to receive only children changed after that timestamp.
|
|
/// The <see cref="ChildrenResponse.Since"/> field contains the timestamp to use for the next delta poll.
|
|
/// </summary>
|
|
[HttpGet("{parentId}/children")]
|
|
[Authorize(Roles = "Admin")]
|
|
public ActionResult<ChildrenResponse> GetChildren(Guid parentId, [FromQuery] DateTime? since) {
|
|
var accessLevel = authService.GetUserData(User)?.AccessLevel ?? EAccessLevel.User;
|
|
if (accessLevel != EAccessLevel.Admin) { return Unauthorized(); }
|
|
var children = jobManager.GetChildren(parentId, since);
|
|
return Ok(new ChildrenResponse { Children = children, Since = DateTime.UtcNow });
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the status of a specific job by its ID.
|
|
/// </summary>
|
|
/// <param name="jobId">The ID of the job.</param>
|
|
/// <returns>The job status, or 404 if not found.</returns>
|
|
[HttpGet("{jobId}")]
|
|
[Authorize(Roles = "Admin")]
|
|
public ActionResult<JobStatusDto> Get(Guid jobId) {
|
|
var accessLevel = authService.GetUserData(User)?.AccessLevel ?? EAccessLevel.User;
|
|
if (accessLevel != EAccessLevel.Admin) { return Unauthorized(); }
|
|
var job = jobManager.GetJobStatus(jobId);
|
|
if (job == null) { return NotFound(); }
|
|
return Ok(job);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Cancels a running or queued job.
|
|
/// </summary>
|
|
/// <param name="jobId">The ID of the job to cancel.</param>
|
|
/// <returns>200 if cancelled, 404 if not found.</returns>
|
|
[HttpDelete("{jobId}")]
|
|
[Authorize(Roles = "Admin")]
|
|
public ActionResult Cancel(Guid jobId) {
|
|
var accessLevel = authService.GetUserData(User)?.AccessLevel ?? EAccessLevel.User;
|
|
|
|
if (accessLevel != EAccessLevel.Admin) { return Unauthorized(); }
|
|
var job = jobManager.GetJob(jobId);
|
|
|
|
if (job == null) { return NotFound(); }
|
|
job.Cancel();
|
|
return Ok();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Enqueues a new job of the specified type.
|
|
/// </summary>
|
|
/// <param name="jobRequest">The job request containing the job type and optional parameters.</param>
|
|
/// <returns>200 if enqueued, 400 if invalid request, 501 if not implemented.</returns>
|
|
[HttpPut]
|
|
[Authorize(Roles = "Admin")]
|
|
public ActionResult Enqueue([FromBody] JobRequestDto jobRequest) {
|
|
var accessLevel = authService.GetUserData(User)?.AccessLevel ?? EAccessLevel.User;
|
|
|
|
if (accessLevel != EAccessLevel.Admin) { return Unauthorized(); }
|
|
|
|
switch (jobRequest.JobType) {
|
|
case EJobType.FileSystemScan:
|
|
if (jobRequest.Parameters.Count != 0)
|
|
return BadRequest("FullFileSystemScan job does not require any parameters.");
|
|
|
|
jobScheduler.QueueFileSystemCrawl(null);
|
|
return Ok();
|
|
case EJobType.ThumbnailGeneration:
|
|
if (jobRequest.Parameters.Count != 0)
|
|
return BadRequest("ThumbnailGeneration job does not require any parameters.");
|
|
|
|
jobScheduler.QueueThumbnailGeneration(null);
|
|
return Ok();
|
|
case EJobType.PreviewGeneration:
|
|
if (jobRequest.Parameters.Count != 0)
|
|
return BadRequest("PreviewGeneration job does not require any parameters.");
|
|
|
|
jobScheduler.QueuePreviewGeneration(null);
|
|
return Ok();
|
|
case EJobType.MetadataExtraction:
|
|
if (jobRequest.Parameters.Count != 0)
|
|
return BadRequest("MetadataExtraction job does not require any parameters.");
|
|
|
|
jobScheduler.QueueMetadataExtraction(null);
|
|
return Ok();
|
|
case EJobType.IntegrityCheck:
|
|
if (jobRequest.Parameters.Count != 0)
|
|
return BadRequest("IntegrityCheck job does not require any parameters.");
|
|
|
|
jobScheduler.QueueIntegrityCheck(null);
|
|
return Ok();
|
|
case EJobType.CreatePersons:
|
|
if (jobRequest.Parameters.Count != 0)
|
|
return BadRequest("CreatePersons job does not require any parameters.");
|
|
jobScheduler.QueueCreatePersons(null);
|
|
return Ok();
|
|
case EJobType.CreateAlbums:
|
|
if (jobRequest.Parameters.Count != 0)
|
|
return BadRequest("CreateAlbums job does not require any parameters.");
|
|
jobScheduler.QueueCreateAlbums(null);
|
|
return Ok();
|
|
case EJobType.PHashGeneration:
|
|
if (jobRequest.Parameters.Count > 1)
|
|
return BadRequest("PHashGeneration job takes zero or one parameter (the asset ID).");
|
|
jobScheduler.QueuePHashGeneration(jobRequest.Parameters.FirstOrDefault());
|
|
return Ok();
|
|
default: return BadRequest("Invalid job type.");
|
|
}
|
|
}
|
|
}
|