Implements Spec 087: Legacy Runs Removal (rigorous). ### What changed - Canonicalized run history: **`operation_runs` is the only run system** for inventory sync, Entra group sync, backup schedule execution/retention/purge. - Removed legacy UI surfaces (Filament Resources / relation managers) for legacy run models. - Legacy run URLs now return **404** (no redirects), with RBAC semantics preserved (404 vs 403 as specified). - Canonicalized affected `operation_runs.type` values (dotted → underscore) via migration. - Drift + inventory references now point to canonical operation runs; includes backfills and then drops legacy FK columns. - Drops legacy run tables after cutover. - Added regression guards to prevent reintroducing legacy run tokens or “backfilling” canonical runs from legacy tables. ### Migrations - `2026_02_12_000001..000006_*` canonicalize types, add/backfill operation_run_id references, drop legacy columns, and drop legacy run tables. ### Tests Focused pack for this spec passed: - `tests/Feature/Guards/NoLegacyRunsTest.php` - `tests/Feature/Guards/NoLegacyRunBackfillTest.php` - `tests/Feature/Operations/LegacyRunRoutesNotFoundTest.php` - `tests/Feature/Monitoring/MonitoringOperationsTest.php` - `tests/Feature/Jobs/RunInventorySyncJobTest.php` ### Notes / impact - Destructive cleanup is handled via migrations (drops legacy tables) after code cutover; deploy should run migrations in the same release. Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #106
90 lines
2.8 KiB
PHP
90 lines
2.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Filament\Widgets\Dashboard;
|
|
|
|
use App\Filament\Resources\FindingResource;
|
|
use App\Models\Finding;
|
|
use App\Models\OperationRun;
|
|
use App\Models\Tenant;
|
|
use App\Support\OpsUx\ActiveRuns;
|
|
use Filament\Facades\Filament;
|
|
use Filament\Widgets\StatsOverviewWidget;
|
|
use Filament\Widgets\StatsOverviewWidget\Stat;
|
|
|
|
class DashboardKpis extends StatsOverviewWidget
|
|
{
|
|
protected static bool $isLazy = false;
|
|
|
|
protected int|string|array $columnSpan = 'full';
|
|
|
|
protected function getPollingInterval(): ?string
|
|
{
|
|
$tenant = Filament::getTenant();
|
|
|
|
if (! $tenant instanceof Tenant) {
|
|
return null;
|
|
}
|
|
|
|
return ActiveRuns::existForTenant($tenant) ? '10s' : null;
|
|
}
|
|
|
|
/**
|
|
* @return array<Stat>
|
|
*/
|
|
protected function getStats(): array
|
|
{
|
|
$tenant = Filament::getTenant();
|
|
|
|
if (! $tenant instanceof Tenant) {
|
|
return [
|
|
Stat::make('Open drift findings', 0),
|
|
Stat::make('High severity drift', 0),
|
|
Stat::make('Active operations', 0),
|
|
Stat::make('Inventory active', 0),
|
|
];
|
|
}
|
|
|
|
$tenantId = (int) $tenant->getKey();
|
|
|
|
$openDriftFindings = (int) Finding::query()
|
|
->where('tenant_id', $tenantId)
|
|
->where('finding_type', Finding::FINDING_TYPE_DRIFT)
|
|
->where('status', Finding::STATUS_NEW)
|
|
->count();
|
|
|
|
$highSeverityDriftFindings = (int) Finding::query()
|
|
->where('tenant_id', $tenantId)
|
|
->where('finding_type', Finding::FINDING_TYPE_DRIFT)
|
|
->where('status', Finding::STATUS_NEW)
|
|
->where('severity', Finding::SEVERITY_HIGH)
|
|
->count();
|
|
|
|
$activeRuns = (int) OperationRun::query()
|
|
->where('tenant_id', $tenantId)
|
|
->active()
|
|
->count();
|
|
|
|
$inventoryActiveRuns = (int) OperationRun::query()
|
|
->where('tenant_id', $tenantId)
|
|
->where('type', 'inventory_sync')
|
|
->active()
|
|
->count();
|
|
|
|
return [
|
|
Stat::make('Open drift findings', $openDriftFindings)
|
|
->url(FindingResource::getUrl('index', tenant: $tenant)),
|
|
Stat::make('High severity drift', $highSeverityDriftFindings)
|
|
->color($highSeverityDriftFindings > 0 ? 'danger' : 'gray')
|
|
->url(FindingResource::getUrl('index', tenant: $tenant)),
|
|
Stat::make('Active operations', $activeRuns)
|
|
->color($activeRuns > 0 ? 'warning' : 'gray')
|
|
->url(route('admin.operations.index')),
|
|
Stat::make('Inventory active', $inventoryActiveRuns)
|
|
->color($inventoryActiveRuns > 0 ? 'warning' : 'gray')
|
|
->url(route('admin.operations.index')),
|
|
];
|
|
}
|
|
}
|