Implements Spec 098: workspace-level settings slices for Backup retention, Drift severity mapping, and Operations retention/threshold. Spec - specs/098-settings-slices-v1-backup-drift-ops/spec.md What changed - Workspace Settings page: grouped Backup/Drift/Operations sections, unset-input UX w/ helper text, per-setting reset actions (confirmed) - Settings registry: adds/updates validation + normalization (incl. drift severity mapping normalization to lowercase) - Backup retention: adds workspace default + floor clamp; job clamps effective keep-last up to floor - Drift findings: optional workspace severity mapping; adds `critical` severity support + badge mapping - Operations pruning: retention computed per workspace via settings; scheduler unchanged; stuck threshold is storage-only Safety / Compliance notes - Filament v5 / Livewire v4: no Livewire v3 usage; relies on existing Filament v5 + Livewire v4 stack - Provider registration unchanged (Laravel 11+/12 uses bootstrap/providers.php) - Destructive actions: per-setting reset uses Filament actions with confirmation - Global search: not affected (no resource changes) - Assets: no new assets registered; no `filament:assets` changes Tests - vendor/bin/sail artisan test --compact tests/Feature/SettingsFoundation/WorkspaceSettingsManageTest.php \ tests/Feature/SettingsFoundation/WorkspaceSettingsViewOnlyTest.php \ tests/Feature/BackupScheduling/BackupScheduleLifecycleTest.php \ tests/Feature/Drift/DriftPolicySnapshotDriftDetectionTest.php \ tests/Feature/Scheduling/PruneOldOperationRunsScheduleTest.php \ tests/Unit/Badges/FindingBadgesTest.php Formatting - vendor/bin/sail bin pint --dirty Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #120
73 lines
1.7 KiB
PHP
73 lines
1.7 KiB
PHP
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
use App\Support\Concerns\DerivesWorkspaceIdFromTenant;
|
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
|
|
class Finding extends Model
|
|
{
|
|
/** @use HasFactory<\Database\Factories\FindingFactory> */
|
|
use DerivesWorkspaceIdFromTenant;
|
|
|
|
use HasFactory;
|
|
|
|
public const string FINDING_TYPE_DRIFT = 'drift';
|
|
|
|
public const string SEVERITY_LOW = 'low';
|
|
|
|
public const string SEVERITY_MEDIUM = 'medium';
|
|
|
|
public const string SEVERITY_HIGH = 'high';
|
|
|
|
public const string SEVERITY_CRITICAL = 'critical';
|
|
|
|
public const string STATUS_NEW = 'new';
|
|
|
|
public const string STATUS_ACKNOWLEDGED = 'acknowledged';
|
|
|
|
protected $guarded = [];
|
|
|
|
protected $casts = [
|
|
'acknowledged_at' => 'datetime',
|
|
'evidence_jsonb' => 'array',
|
|
];
|
|
|
|
public function tenant(): BelongsTo
|
|
{
|
|
return $this->belongsTo(Tenant::class);
|
|
}
|
|
|
|
public function baselineRun(): BelongsTo
|
|
{
|
|
return $this->belongsTo(OperationRun::class, 'baseline_operation_run_id');
|
|
}
|
|
|
|
public function currentRun(): BelongsTo
|
|
{
|
|
return $this->belongsTo(OperationRun::class, 'current_operation_run_id');
|
|
}
|
|
|
|
public function acknowledgedByUser(): BelongsTo
|
|
{
|
|
return $this->belongsTo(User::class, 'acknowledged_by_user_id');
|
|
}
|
|
|
|
public function acknowledge(User $user): void
|
|
{
|
|
if ($this->status === self::STATUS_ACKNOWLEDGED) {
|
|
return;
|
|
}
|
|
|
|
$this->forceFill([
|
|
'status' => self::STATUS_ACKNOWLEDGED,
|
|
'acknowledged_at' => now(),
|
|
'acknowledged_by_user_id' => $user->getKey(),
|
|
]);
|
|
|
|
$this->save();
|
|
}
|
|
}
|