|
- using System.Security.Claims;
- using Campaign_Tracker.Server.Audit;
- using Campaign_Tracker.Server.Authorization;
- using Campaign_Tracker.Server.ExtensionData;
- using Microsoft.AspNetCore.Authorization;
- using Microsoft.AspNetCore.Mvc;
-
- namespace Campaign_Tracker.Server.Controllers;
-
- /// <summary>
- /// Admin API for the extension-to-legacy referential integrity check (Story 1.8 AC #4).
- /// </summary>
- [ApiController]
- [Authorize(Policy = ApplicationPolicy.AdminAccess)]
- [Route("api/admin/legacy-link")]
- public sealed class LegacyLinkController : ControllerBase
- {
- private readonly ILegacyLinkIntegrityCheck _integrityCheck;
- private readonly IAuditService _audit;
- private readonly TimeProvider _timeProvider;
-
- public LegacyLinkController(
- ILegacyLinkIntegrityCheck integrityCheck,
- IAuditService audit,
- TimeProvider timeProvider)
- {
- _integrityCheck = integrityCheck;
- _audit = audit;
- _timeProvider = timeProvider;
- }
-
- /// <summary>
- /// Runs the extension-to-legacy link integrity check on demand and returns a report.
- /// Intended to be called by a scheduler for nightly runs and by admins for manual runs.
- /// </summary>
- [HttpPost("integrity-check")]
- public async Task<ActionResult<LegacyLinkIntegrityResponse>> RunIntegrityCheck(
- CancellationToken cancellationToken)
- {
- var report = await _integrityCheck.CheckAsync(cancellationToken);
-
- var actor = User.Identity?.Name
- ?? User.FindFirstValue(ClaimTypes.NameIdentifier)
- ?? "unknown";
-
- _audit.Record(new AuditEvent(
- EventType: report.IsConsistent
- ? "LEGACY_LINK_INTEGRITY_PASSED"
- : "LEGACY_LINK_INTEGRITY_FAILED",
- ActorIdentity: actor,
- Resource: "legacy-link/integrity-check",
- Outcome: $"{report.ConsistentRecords}/{report.TotalRecords} consistent ({report.ConsistencyPercentage:F2}%)",
- TraceIdentifier: HttpContext.TraceIdentifier,
- RecordedAt: _timeProvider.GetUtcNow()));
-
- return Ok(LegacyLinkIntegrityResponse.From(report));
- }
- }
-
- public sealed record LegacyLinkIntegrityResponse(
- bool IsConsistent,
- DateTimeOffset CheckedAt,
- int TotalRecords,
- int ConsistentRecords,
- int FailedRecords,
- double ConsistencyPercentage,
- IReadOnlyList<LegacyLinkFailureResponse> Failures)
- {
- public static LegacyLinkIntegrityResponse From(LegacyLinkIntegrityReport report) =>
- new(report.IsConsistent,
- report.CheckedAt,
- report.TotalRecords,
- report.ConsistentRecords,
- report.FailedRecords,
- report.ConsistencyPercentage,
- report.Failures
- .Select(f => new LegacyLinkFailureResponse(
- f.RecordType, f.RecordId,
- f.Reference.Type.ToString(), f.Reference.Value,
- f.Reason))
- .ToArray());
- }
-
- public sealed record LegacyLinkFailureResponse(
- string RecordType,
- string RecordId,
- string LinkType,
- string LinkValue,
- string Reason);
|