setDescription("Cleanup pending photo uploads older than specified time") ->addOption( "max-age", "a", InputOption::VALUE_OPTIONAL, "Maximum age in hours (default: 24)", 24 ) ->addOption( "dry-run", "d", InputOption::VALUE_NONE, "Show what would be deleted without actually deleting" ); } protected function execute(InputInterface $input, OutputInterface $output): int { $maxAge = (int) $input->getOption("max-age"); $dryRun = $input->getOption("dry-run"); $photoFolder = __DIR__ . "/../tmp/api-storage/photos"; if (!is_dir($photoFolder)) { $output->writeln("Photo upload directory not found: {$photoFolder}"); return Command::FAILURE; } $output->writeln("Scanning for pending uploads older than {$maxAge} hours..."); $cutoffTime = time() - ($maxAge * 3600); $deletedCount = 0; $totalSize = 0; $files = glob($photoFolder . "/*_*.oct"); foreach ($files as $file) { $fileTime = filemtime($file); if ($fileTime < $cutoffTime) { $fileSize = filesize($file); $totalSize += $fileSize; if ($dryRun) { $age = round((time() - $fileTime) / 3600, 1); $output->writeln("Would delete: " . basename($file) . " (age: {$age}h, size: " . $this->formatBytes($fileSize) . ")"); } else { if (unlink($file)) { $deletedCount++; $output->writeln("Deleted: " . basename($file) . ""); } else { $output->writeln("Failed to delete: " . basename($file) . ""); } } } } if ($dryRun) { $output->writeln("Dry run completed. Would delete {$deletedCount} files (" . $this->formatBytes($totalSize) . ")"); } else { $output->writeln("Cleanup completed. Deleted {$deletedCount} files (" . $this->formatBytes($totalSize) . ")"); } return Command::SUCCESS; } private function formatBytes(int $bytes): string { $units = ['B', 'KB', 'MB', 'GB']; $bytes = max($bytes, 0); $pow = floor(($bytes ? log($bytes) : 0) / log(1024)); $pow = min($pow, count($units) - 1); $bytes /= pow(1024, $pow); return round($bytes, 2) . ' ' . $units[$pow]; } }