feat: complete Nexus mission-control workflows
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Security.Claims;
|
||||
using Nexus.Api.Data;
|
||||
using Nexus.Api.DTOs;
|
||||
using Nexus.Api.Models;
|
||||
using Nexus.Api.Repositories;
|
||||
using Nexus.Api.Services;
|
||||
|
||||
namespace Nexus.Api.Controllers;
|
||||
@@ -10,7 +12,11 @@ namespace Nexus.Api.Controllers;
|
||||
[Authorize]
|
||||
[ApiController]
|
||||
[Route("api/v1/tasks")]
|
||||
public class TasksController(ITaskService taskService, IAgentService agentService, IConfiguration configuration) : ControllerBase
|
||||
public class TasksController(
|
||||
ITaskService taskService,
|
||||
IAgentService agentService,
|
||||
IConfiguration configuration,
|
||||
IActivityRepository activityRepository) : ControllerBase
|
||||
{
|
||||
[HttpGet]
|
||||
public async Task<IResult> GetAll(CancellationToken ct)
|
||||
@@ -27,6 +33,7 @@ public class TasksController(ITaskService taskService, IAgentService agentServic
|
||||
}
|
||||
|
||||
[HttpGet("pending-approval")]
|
||||
[Authorize(Roles = "owner")]
|
||||
public async Task<IResult> GetPendingApproval(CancellationToken ct)
|
||||
{
|
||||
var pending = await taskService.GetPendingApprovalAsync(ct);
|
||||
@@ -34,9 +41,11 @@ public class TasksController(ITaskService taskService, IAgentService agentServic
|
||||
}
|
||||
|
||||
[HttpPost("{id:guid}/approve")]
|
||||
[Authorize(Roles = "owner")]
|
||||
public async Task<IResult> Approve(Guid id, CancellationToken ct)
|
||||
{
|
||||
var result = await taskService.ApproveAsync(id, ct);
|
||||
await WriteApprovalAuditAsync(id, "approve", result.Outcome, result.Task?.State, ct);
|
||||
return result.Outcome switch
|
||||
{
|
||||
TaskOperationOutcome.NotFound => Results.NotFound(),
|
||||
@@ -49,9 +58,11 @@ public class TasksController(ITaskService taskService, IAgentService agentServic
|
||||
}
|
||||
|
||||
[HttpPost("{id:guid}/reject")]
|
||||
[Authorize(Roles = "owner")]
|
||||
public async Task<IResult> Reject(Guid id, CancellationToken ct)
|
||||
{
|
||||
var result = await taskService.RejectAsync(id, ct);
|
||||
await WriteApprovalAuditAsync(id, "reject", result.Outcome, result.Task?.State, ct);
|
||||
return result.Outcome switch
|
||||
{
|
||||
TaskOperationOutcome.NotFound => Results.NotFound(),
|
||||
@@ -160,4 +171,30 @@ public class TasksController(ITaskService taskService, IAgentService agentServic
|
||||
var count = await taskService.ResetStaleAsync(request.StaleHours, ct);
|
||||
return Results.Ok(new ResetStaleResponse(count));
|
||||
}
|
||||
|
||||
private async Task WriteApprovalAuditAsync(
|
||||
Guid taskId,
|
||||
string action,
|
||||
TaskOperationOutcome outcome,
|
||||
string? state,
|
||||
CancellationToken ct)
|
||||
{
|
||||
await activityRepository.AddAsync(new ActivityEvent
|
||||
{
|
||||
Type = "task_approval_audit",
|
||||
Message = $"Task approval task={taskId} action={action} caller={DescribeCaller(HttpContext.User)} outcome={outcome} checkpoint={(state ?? "none")}",
|
||||
TaskId = taskId
|
||||
}, ct);
|
||||
}
|
||||
|
||||
private static string DescribeCaller(ClaimsPrincipal user)
|
||||
{
|
||||
var subject = user.FindFirst(ClaimTypes.NameIdentifier)?.Value
|
||||
?? user.FindFirst(ClaimTypes.Email)?.Value
|
||||
?? user.Identity?.Name
|
||||
?? "unknown";
|
||||
|
||||
var role = user.FindFirst(ClaimTypes.Role)?.Value ?? "owner";
|
||||
return $"{role}:{subject}".ToLowerInvariant();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user