feat/Update_Controllers_to_Allow_for_Premium_Membership

This commit is contained in:
cesnimda
2026-08-03 09:17:28 +02:00
parent de937d25dc
commit c3f4a57195
187 changed files with 26062 additions and 991 deletions
@@ -0,0 +1,129 @@
using JobTrackerApi.Models;
using JobTrackerApi.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace JobTrackerApi.Controllers;
[ApiController]
[Route("api/operations")]
[Authorize(AuthenticationSchemes = "local")]
public sealed class OperationsController(UserOperationStore operations) : ControllerBase
{
[HttpGet]
public async Task<ActionResult<IReadOnlyList<OperationDto>>> List([FromQuery] int limit = 25, CancellationToken cancellationToken = default)
{
if (limit is < 1 or > 100) return BadRequest(new { code = "invalid_limit", message = "Limit must be between 1 and 100." });
return Ok((await operations.ListAsync(limit, cancellationToken)).Select(ToDto).ToList());
}
[HttpGet("{id:guid}")]
public async Task<ActionResult<OperationDto>> Get(Guid id, CancellationToken cancellationToken)
{
var operation = await operations.GetAsync(id, cancellationToken);
return operation is null ? NotFound() : Ok(ToDto(operation));
}
[HttpPost("{id:guid}/cancel")]
public async Task<ActionResult<OperationDto>> Cancel(Guid id, CancellationToken cancellationToken)
{
if (await operations.GetAsync(id, cancellationToken) is null) return NotFound();
if (!await operations.RequestCancellationAsync(id, cancellationToken))
return Conflict(new { code = "operation_not_cancellable", message = "This operation can no longer be cancelled." });
return Ok(ToDto((await operations.GetAsync(id, cancellationToken))!));
}
[HttpPost("{id:guid}/retry")]
public async Task<ActionResult<OperationDto>> Retry(Guid id, CancellationToken cancellationToken)
{
if (await operations.GetAsync(id, cancellationToken) is null) return NotFound();
if (!await operations.RetryAsync(id, cancellationToken))
return Conflict(new { code = "operation_not_retryable", message = "Only failed or cancelled operations can be retried." });
return Ok(ToDto((await operations.GetAsync(id, cancellationToken))!));
}
private static OperationDto ToDto(UserOperation operation) => new(
operation.Id,
operation.TaskType,
operation.Status,
operation.SubjectType,
operation.CreatedAtUtc,
operation.StartedAtUtc,
operation.CompletedAtUtc,
operation.DeadlineAtUtc,
operation.CancellationRequestedAtUtc,
operation.ProgressStage,
operation.ProgressPercent,
operation.FailureCategory,
!OperationStatuses.IsTerminal(operation.Status) && operation.CancellationRequestedAtUtc is null,
operation.Status is OperationStatuses.Failed or OperationStatuses.Cancelled);
}
public sealed record OperationDto(
Guid Id,
string TaskType,
string Status,
string? SubjectType,
DateTime CreatedAtUtc,
DateTime? StartedAtUtc,
DateTime? CompletedAtUtc,
DateTime? DeadlineAtUtc,
DateTime? CancellationRequestedAtUtc,
string? ProgressStage,
int? ProgressPercent,
string? FailureCategory,
bool CanCancel,
bool CanRetry);
[ApiController]
[Route("api/notifications")]
[Authorize(AuthenticationSchemes = "local")]
public sealed class NotificationsController(UserNotificationStore notifications) : ControllerBase
{
[HttpGet]
public async Task<ActionResult<IReadOnlyList<NotificationDto>>> List([FromQuery] int limit = 25, CancellationToken cancellationToken = default)
{
if (limit is < 1 or > 100) return BadRequest(new { code = "invalid_limit", message = "Limit must be between 1 and 100." });
return Ok((await notifications.ListAsync(limit, cancellationToken)).Select(ToDto).ToList());
}
[HttpGet("unread-count")]
public async Task<IActionResult> UnreadCount(CancellationToken cancellationToken) =>
Ok(new { count = await notifications.UnreadCountAsync(cancellationToken) });
[HttpPost("{id:guid}/read")]
public async Task<IActionResult> MarkRead(Guid id, CancellationToken cancellationToken)
{
if (await notifications.GetAsync(id, cancellationToken) is null) return NotFound();
await notifications.MarkReadAsync(id, cancellationToken);
return NoContent();
}
[HttpDelete("{id:guid}")]
public async Task<IActionResult> Dismiss(Guid id, CancellationToken cancellationToken)
{
if (await notifications.GetAsync(id, cancellationToken) is null) return NotFound();
await notifications.DismissAsync(id, cancellationToken);
return NoContent();
}
private static NotificationDto ToDto(UserNotification notification) => new(
notification.Id,
notification.OperationId,
notification.Kind,
notification.Title,
notification.Message,
notification.LinkPath,
notification.CreatedAtUtc,
notification.ReadAtUtc);
}
public sealed record NotificationDto(
Guid Id,
Guid? OperationId,
string Kind,
string Title,
string Message,
string? LinkPath,
DateTime CreatedAtUtc,
DateTime? ReadAtUtc);