PR 4: Production guardrails for Seed Data and Storage Migration

- SeedData/Index: added prominent danger banner when running in Production (environment include="Production") so operators are clearly warned before writing to the live database; page remains accessible since seeding is occasionally valid in prod for new company onboarding
- StorageMigration/Index: added warning banner in Production explaining the tool is not needed now that Azure Blob migration is complete
- PlatformAdminController: hide Storage Migration hub card in Production via ShowStorageMigration flag (same WEBSITE_SITE_NAME pattern as ShowRawLogFiles); Seed Data card remains visible so prod onboarding stays reachable

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-12 21:08:14 -04:00
parent e9cd67f5d9
commit 637be701ea
3 changed files with 38 additions and 10 deletions
@@ -9,6 +9,7 @@ namespace PowderCoating.Web.Controllers;
public class PlatformAdminController : Controller
{
private static readonly bool ShowRawLogFiles = string.IsNullOrEmpty(Environment.GetEnvironmentVariable("WEBSITE_SITE_NAME"));
private static readonly bool ShowStorageMigration = string.IsNullOrEmpty(Environment.GetEnvironmentVariable("WEBSITE_SITE_NAME"));
public IActionResult TenantsBilling() => View(BuildTenantsBillingHub());
@@ -94,21 +95,28 @@ public class PlatformAdminController : Controller
};
}
private static PlatformAdminHubViewModel BuildMaintenanceHub() => new()
private static PlatformAdminHubViewModel BuildMaintenanceHub()
{
Title = "Maintenance",
PageIcon = "bi-wrench-adjustable-circle",
Intro = "Use these tools for exceptional maintenance work, migration tasks, and destructive admin operations. They are not routine day-to-day workflows.",
WarningTitle = "Use With Care",
WarningMessage = "These tools can expose bulk data, change platform state, or permanently remove records. Use them deliberately and preferably with a written reason or ticket.",
Cards = new List<PlatformAdminLinkCardViewModel>
var cards = new List<PlatformAdminLinkCardViewModel>
{
Card("Data Export", "Export a tenant company's data set for audits, offboarding, support, or migration work.", "DataExport", "Index", "bi-file-earmark-arrow-down", "Maintenance", SubtleBadge("warning")),
Card("Data Purge", "Permanently delete soft-deleted records after previewing impact and cutoff windows.", "DataPurge", "Index", "bi-trash3", "Dangerous", SubtleBadge("danger")),
Card("Storage Migration", "Run one-off migration of local media files into cloud storage.", "StorageMigration", "Index", "bi-cloud-upload", "One-off", SubtleBadge("info")),
Card("Seed Data", "Seed or remove system and demo data for setup, QA, or controlled test scenarios.", "SeedData", "Index", "bi-database-fill-gear", "Restricted", SubtleBadge("danger"))
}
};
};
if (ShowStorageMigration)
cards.Insert(2, Card("Storage Migration", "Run one-off migration of local media files into cloud storage.", "StorageMigration", "Index", "bi-cloud-upload", "One-off", SubtleBadge("info")));
return new PlatformAdminHubViewModel
{
Title = "Maintenance",
PageIcon = "bi-wrench-adjustable-circle",
Intro = "Use these tools for exceptional maintenance work, migration tasks, and destructive admin operations. They are not routine day-to-day workflows.",
WarningTitle = "Use With Care",
WarningMessage = "These tools can expose bulk data, change platform state, or permanently remove records. Use them deliberately and preferably with a written reason or ticket.",
Cards = cards
};
}
private static PlatformAdminLinkCardViewModel Card(
string title,
@@ -24,6 +24,16 @@
</a>
</div>
<environment include="Production">
<div class="alert alert-danger alert-permanent d-flex gap-3 align-items-start mb-4">
<i class="bi bi-exclamation-octagon-fill fs-4 flex-shrink-0 mt-1 text-danger"></i>
<div>
<div class="fw-semibold">You are running in Production</div>
<div class="small mt-1">Seed operations write data directly to the live database. Only proceed if you have a specific, intentional reason — for example, onboarding a new company. Do not seed the default demo company in production.</div>
</div>
</div>
</environment>
@if (TempData["SuccessMessage"] != null)
{
<div class="alert alert-success alert-dismissible alert-permanent fade show" role="alert">
@@ -12,6 +12,16 @@
</a>
</div>
<environment include="Production">
<div class="alert alert-warning alert-permanent d-flex gap-3 align-items-start mb-4">
<i class="bi bi-exclamation-triangle-fill fs-4 flex-shrink-0 mt-1"></i>
<div>
<div class="fw-semibold">This tool is not needed in Production</div>
<div class="small mt-1">The platform has already migrated to Azure Blob Storage. Running this migration again in production is unnecessary and may cause unintended side effects. If you believe there is a specific reason to proceed, verify with the team first.</div>
</div>
</div>
</environment>
<div class="row g-4">
<!-- Status Card -->
<div class="col-md-6">