feat(work-orders): add legacy deprecation middleware and Blazor WO sunset guard

This commit is contained in:
Arthur Bassi 2026-07-13 13:22:53 -03:00
parent 89cb520db9
commit 64ca9e0bdf
6 changed files with 111 additions and 3 deletions

View file

@ -0,0 +1,57 @@
using Api.SeaHavenIndustries.Options;
using Microsoft.Extensions.Options;
namespace Api.SeaHavenIndustries.Middleware
{
/// <summary>Adds Sunset/Deprecation headers to legacy WorkOrder REST routes.</summary>
public class LegacyDeprecationMiddleware
{
private static readonly HashSet<string> LegacyPathSegments = new(StringComparer.OrdinalIgnoreCase)
{
"GetWorkOrderList",
"AddWorkorder",
"EditWorkorder",
"ChangeStatus",
"ChangeAssignment",
"DeleteWorkorder",
"GetWorkorderById"
};
private readonly RequestDelegate _next;
private readonly LegacyEndpointOptions _options;
public LegacyDeprecationMiddleware(RequestDelegate next, IOptions<LegacyEndpointOptions> options)
{
_next = next;
_options = options.Value;
}
public async Task InvokeAsync(HttpContext context)
{
if (_options.DeprecationEnabled)
{
var path = context.Request.Path.Value ?? "";
var isLegacyWo = (path.Contains("/WorkOrder", StringComparison.OrdinalIgnoreCase)
|| path.Contains("/workorders", StringComparison.OrdinalIgnoreCase))
&& LegacyPathSegments.Any(seg => path.Contains(seg, StringComparison.OrdinalIgnoreCase));
if (isLegacyWo)
{
context.Response.OnStarting(() =>
{
context.Response.Headers["Deprecation"] = "true";
if (!string.IsNullOrWhiteSpace(_options.SunsetDate)
&& DateTimeOffset.TryParse(_options.SunsetDate, out var sunset))
{
context.Response.Headers["Sunset"] = sunset.UtcDateTime.ToString("r");
}
context.Response.Headers["Link"] = "</api/workorders/board>; rel=\"successor-version\"";
return Task.CompletedTask;
});
}
}
await _next(context);
}
}
}

View file

@ -1,5 +1,6 @@
using Api.SeaHavenIndustries.Helper;
using Api.SeaHavenIndustries.Helper;
using Api.SeaHavenIndustries.HostedServices;
using Api.SeaHavenIndustries.Middleware;
using Api.SeaHavenIndustries.Options;
using Data.SeaHavenIndustries;
using Microsoft.AspNetCore.Authentication.JwtBearer;
@ -151,6 +152,7 @@ if (!app.Environment.IsDevelopment())
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCors();
app.UseMiddleware<LegacyDeprecationMiddleware>();
app.UseAuthentication();
app.UseAuthorization();
@ -213,4 +215,3 @@ app.MapControllers();
//}
app.Run();

View file

@ -22,14 +22,22 @@ namespace SeaHavenIndustries.Data.Services
private UserManager<ApplicationUser> _userManager;
private UploadFileHp _uploadFileHelper;
private readonly SendMessage _sendemail;
private readonly BlazorWorkOrderSunsetGuard _sunsetGuard;
public WorkorderService(SendMessage sendemail, ApplicationDbContext context, IHttpContextAccessor httpContextAccessor, UserManager<ApplicationUser> userManager, UploadFileHp uploadFileHelper)
public WorkorderService(
SendMessage sendemail,
ApplicationDbContext context,
IHttpContextAccessor httpContextAccessor,
UserManager<ApplicationUser> userManager,
UploadFileHp uploadFileHelper,
BlazorWorkOrderSunsetGuard sunsetGuard)
{
_context = context;
_httpContextAccessor = httpContextAccessor;
_userManager = userManager;
_uploadFileHelper = uploadFileHelper;
_sendemail = sendemail;
_sunsetGuard = sunsetGuard;
}
public async Task<List<WorkOrder>> Getworkorder()
{
@ -178,6 +186,7 @@ namespace SeaHavenIndustries.Data.Services
}
public async Task<string> Addworkorder(WorkOrder model, List<FileUploadViewModel> files)
{
_sunsetGuard.ThrowIfMutationsBlocked();
try
{
_context.ChangeTracker.Clear();
@ -425,6 +434,7 @@ namespace SeaHavenIndustries.Data.Services
}
public async Task<string> Deleteworkorder(int id)
{
_sunsetGuard.ThrowIfMutationsBlocked();
_context.ChangeTracker.Clear();
try

View file

@ -0,0 +1,26 @@
using Microsoft.Extensions.Options;
using SeaHavenIndustries.Options;
namespace SeaHavenIndustries.Helper
{
public class BlazorWorkOrderSunsetGuard
{
private readonly BlazorWorkOrderSunsetOptions _options;
public BlazorWorkOrderSunsetGuard(IOptions<BlazorWorkOrderSunsetOptions> options)
{
_options = options.Value;
}
public bool IsSunsetEnabled => _options.Enabled;
public string ShocBaseUrl => _options.ShocBaseUrl.TrimEnd('/');
public void ThrowIfMutationsBlocked()
{
if (_options.Enabled)
throw new InvalidOperationException(
"Work Orders module has moved to SHOC. Editing via Blazor is disabled.");
}
}
}

View file

@ -0,0 +1,11 @@
namespace SeaHavenIndustries.Options
{
public class BlazorWorkOrderSunsetOptions
{
public const string SectionName = "BlazorWorkOrderSunset";
public bool Enabled { get; set; }
public string ShocBaseUrl { get; set; } = "https://shoc.seahaven.com";
}
}

View file

@ -77,6 +77,9 @@ builder.Services.AddScoped<UploadFileHp>();
builder.Services.AddScoped<NorthwindService>();
builder.Services.AddScoped<SendMessage>();
builder.Services.AddScoped<PdfService>();
builder.Services.Configure<SeaHavenIndustries.Options.BlazorWorkOrderSunsetOptions>(
builder.Configuration.GetSection(SeaHavenIndustries.Options.BlazorWorkOrderSunsetOptions.SectionName));
builder.Services.AddSingleton<BlazorWorkOrderSunsetGuard>();
builder.Services.AddSession(options =>
{