#!/usr/bin/env php connect_error) { die("Database connection failed: " . $conn->connect_error . "\n"); } $uploadsDir = dirname(__DIR__) . '/uploads'; $dryRun = in_array('--dry-run', $argv); if ($dryRun) { echo "DRY RUN MODE - No files will be deleted\n"; } echo "Scanning uploads directory: $uploadsDir\n"; // Get all valid ticket IDs from database $ticketIds = []; $result = $conn->query("SELECT ticket_id FROM tickets"); while ($row = $result->fetch_assoc()) { $ticketIds[] = $row['ticket_id']; } echo "Found " . count($ticketIds) . " tickets in database\n"; // Get all attachment records $attachments = []; $result = $conn->query("SELECT ticket_id, filename FROM ticket_attachments"); if ($result) { while ($row = $result->fetch_assoc()) { $key = $row['ticket_id'] . '/' . $row['filename']; $attachments[$key] = true; } } echo "Found " . count($attachments) . " attachment records in database\n"; // Scan uploads directory $orphanedFolders = []; $orphanedFiles = []; $totalSize = 0; $ticketDirs = glob($uploadsDir . '/*', GLOB_ONLYDIR); foreach ($ticketDirs as $ticketDir) { $ticketId = basename($ticketDir); // Skip non-ticket directories if (!preg_match('/^\d{9}$/', $ticketId)) { continue; } // Check if ticket exists if (!in_array($ticketId, $ticketIds)) { // Ticket doesn't exist - entire folder is orphaned $orphanedFolders[] = $ticketDir; $folderSize = 0; foreach (glob($ticketDir . '/*') as $file) { if (is_file($file)) { $folderSize += filesize($file); } } $totalSize += $folderSize; echo "Orphan folder (ticket deleted): $ticketDir (" . formatBytes($folderSize) . ")\n"; continue; } // Check individual files $files = glob($ticketDir . '/*'); foreach ($files as $file) { if (is_file($file)) { $filename = basename($file); $key = $ticketId . '/' . $filename; if (!isset($attachments[$key])) { $orphanedFiles[] = $file; $fileSize = filesize($file); $totalSize += $fileSize; echo "Orphan file (no DB record): $file (" . formatBytes($fileSize) . ")\n"; } } } } echo "\n=== Summary ===\n"; echo "Orphaned folders: " . count($orphanedFolders) . "\n"; echo "Orphaned files: " . count($orphanedFiles) . "\n"; echo "Total size to recover: " . formatBytes($totalSize) . "\n"; if (!$dryRun && ($orphanedFolders || $orphanedFiles)) { echo "\nDeleting orphaned items...\n"; foreach ($orphanedFiles as $file) { if (unlink($file)) { echo "Deleted: $file\n"; } else { echo "Failed to delete: $file\n"; } } foreach ($orphanedFolders as $folder) { deleteDirectory($folder); echo "Deleted folder: $folder\n"; } echo "Cleanup complete!\n"; } elseif ($dryRun) { echo "\nRun without --dry-run to delete these items.\n"; } else { echo "\nNo orphaned items found.\n"; } $conn->close(); function formatBytes($bytes) { if ($bytes >= 1073741824) { return number_format($bytes / 1073741824, 2) . ' GB'; } elseif ($bytes >= 1048576) { return number_format($bytes / 1048576, 2) . ' MB'; } elseif ($bytes >= 1024) { return number_format($bytes / 1024, 2) . ' KB'; } else { return $bytes . ' bytes'; } } function deleteDirectory($dir) { if (!is_dir($dir)) return; $files = array_diff(scandir($dir), ['.', '..']); foreach ($files as $file) { $path = "$dir/$file"; is_dir($path) ? deleteDirectory($path) : unlink($path); } rmdir($dir); }