option('disk'); $maxAgeDays = (int) $this->option('max-age'); $minBytes = (int) $this->option('min-size'); $this->info("Verifying backups on disk: {$disk}"); $storage = Storage::disk($disk); if (! $storage->exists('Laravel')) { $reason = 'Backup directory "Laravel" not found on disk.'; $this->error($reason); $this->notifyFailure($reason, $disk, $telegram); return self::FAILURE; } $files = collect($storage->allFiles('Laravel')) ->filter(fn ($f) => Str::endsWith($f, ['.zip', '.gz', '.sql'])) ->sortByDesc(fn ($f) => $storage->lastModified($f)) ->values(); if ($files->isEmpty()) { $reason = 'No backup files found.'; $this->error($reason); $this->notifyFailure($reason, $disk, $telegram); return self::FAILURE; } $latest = $files->first(); $latestModified = $storage->lastModified($latest); $latestSize = $storage->size($latest); $ageDays = (int) round((time() - $latestModified) / 86400); $this->table(['File', 'Age (days)', 'Size (KB)', 'Status'], [[ basename($latest), $ageDays, round($latestSize / 1024, 1), $this->statusLabel($ageDays, $latestSize, $maxAgeDays, $minBytes), ]]); $problems = []; if ($ageDays > $maxAgeDays) { $problems[] = "Latest backup is {$ageDays} days old (threshold: {$maxAgeDays} days)"; } if ($latestSize < $minBytes) { $problems[] = "Latest backup is suspiciously small ({$latestSize} bytes)"; } if (! empty($problems)) { foreach ($problems as $problem) { $this->warn($problem); } $this->notifyFailure(implode('; ', $problems), $disk, $telegram); return self::FAILURE; } $this->info("✓ Backup verification passed. {$files->count()} backup(s) found."); Log::channel('single')->info('[VerifyBackups] OK', ['count' => $files->count(), 'age_days' => $ageDays]); return self::SUCCESS; } private function statusLabel(int $age, int $size, int $maxAge, int $minBytes): string { if ($age > $maxAge || $size < $minBytes) { return '⚠ WARN'; } return '✓ OK'; } private function notifyFailure(string $reason, string $disk, TelegramService $telegram): void { Log::channel('single')->error('[VerifyBackups] FAILED', compact('reason', 'disk')); // Prepare Telegram Alert $hostname = gethostname(); $message = "🚨 BACKUP VERIFICATION FAILED 🚨\n"; $message .= "--------------------------------\n"; $message .= "Host: {$hostname}\n"; $message .= "Disk: {$disk}\n"; $message .= "Reason: {$reason}\n"; $message .= "--------------------------------\n"; $message .= 'Please check the backup logs immediately!'; $telegram->sendMessage($message); // Notify developer role via database notification try { $developers = User::role('Developer')->get(); foreach ($developers as $dev) { $dev->notify(new BackupFailedNotification($reason)); } } catch (\Throwable) { // Silently skip if notification classes don't exist yet } } }