shoc-backend/Api.SeaHavenIndustries/Controllers/DispatchPublicController.cs
Adam Moussa 73be673444 Add vendor portal with token-based authentication
- VendorAccessToken model + unique-index migration; TokenLifetimeDays config
- VendorPortalTokenService: CSPRNG token generation, rotation, revocation
- VendorPortalController: public portal API guarded by X-Vendor-Token header;
  dispatches list/detail, accept, vendor status transitions, cancel request,
  checklist updates, signoffs (vendor + customer), comments with dispatcher
  attribution via AspNetUsers join
- VendorController: portal-token admin endpoints (get / rotate / revoke)
- WorkOrderController: dispatch email now uses vendor portal URL and HTML-encodes
  user fields; AddDispatchComment now stores CommentType='dispatcher' with the
  SHOC user's name so portal can attribute the author
- DispatchPublicController: deprecated per-dispatch GET accept flow returns a
  static 'link no longer active' page (no state mutation)
2026-04-20 12:01:53 -04:00

43 lines
1.7 KiB
C#

using Microsoft.AspNetCore.Mvc;
namespace Api.SeaHavenIndustries.Controllers
{
[ApiController]
[Route("api/dispatch")]
public class DispatchPublicController : Controller
{
[HttpGet("accept/{token}")]
public IActionResult Accept(string token)
{
return Content(BuildHtml(), "text/html");
}
private static string BuildHtml()
{
return @"<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'/>
<meta name='viewport' content='width=device-width, initial-scale=1'/>
<title>Link Expired - Sea Haven Industries</title>
<style>
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; display: flex; justify-content: center; align-items: center; min-height: 100vh; margin: 0; background: #f5f5f5; }
.card { background: white; border-radius: 12px; padding: 40px; max-width: 480px; text-align: center; box-shadow: 0 4px 20px rgba(0,0,0,0.1); }
.icon { width: 60px; height: 60px; border-radius: 50%; background: #ffc107; color: white; font-size: 28px; display: flex; align-items: center; justify-content: center; margin: 0 auto 20px; }
h1 { font-size: 22px; color: #333; margin: 0 0 12px; }
p { font-size: 15px; color: #666; line-height: 1.6; margin: 0; }
.footer { margin-top: 30px; font-size: 12px; color: #999; }
</style>
</head>
<body>
<div class='card'>
<div class='icon'>!</div>
<h1>Link No Longer Active</h1>
<p>This dispatch accept link has been retired. Please use the vendor portal link in your most recent dispatch email, or contact your dispatcher for a new link.</p>
<div class='footer'>Sea Haven Industries</div>
</div>
</body>
</html>";
}
}
}