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
}
}
}