false,'error'=>'Forbidden']); else { http_response_code(403); echo 'Forbidden'; } exit; } $backupDir = '/home/tomtomgames.com/backups'; if (!is_dir($backupDir)) @mkdir($backupDir, 0750, true); switch ($action) { case 'list': $files = glob($backupDir . '/ttg_backup_*.zip') ?: []; rsort($files); $backups = array_map(fn($f) => [ 'name' => basename($f), 'size' => filesize($f), 'created' => date('Y-m-d H:i:s', filemtime($f)), ], $files); echo json_encode(['success'=>true, 'backups'=>$backups]); break; case 'create': if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false,'error'=>'POST required']); exit; } set_time_limit(300); ignore_user_abort(true); $date = date('Y-m-d_H-i-s'); $sqlFile = "/tmp/ttg_db_{$date}.sql"; $zipFile = "{$backupDir}/ttg_backup_{$date}.zip"; $siteDir = '/home/tomtomgames.com/public_html'; // Export database $dbCmd = sprintf( '/usr/bin/mysqldump -u %s -p%s %s > %s 2>&1', escapeshellarg(DB_USER), escapeshellarg(DB_PASS), escapeshellarg(DB_NAME), escapeshellarg($sqlFile) ); exec($dbCmd, $dbOut, $dbRc); if ($dbRc !== 0 || !file_exists($sqlFile) || filesize($sqlFile) < 10) { @unlink($sqlFile); echo json_encode(['success'=>false,'error'=>'Database export failed']); exit; } // Zip site files + db dump into one archive $zipCmd = sprintf( '/usr/bin/zip -r %s %s %s -x "*/backups/*" 2>&1', escapeshellarg($zipFile), escapeshellarg($siteDir), escapeshellarg($sqlFile) ); exec($zipCmd, $zipOut, $zipRc); @unlink($sqlFile); if ($zipRc !== 0 || !file_exists($zipFile)) { @unlink($zipFile); echo json_encode(['success'=>false,'error'=>'Archive creation failed']); exit; } // Prune — keep only the 7 most recent $all = glob($backupDir . '/ttg_backup_*.zip') ?: []; rsort($all); foreach (array_slice($all, 7) as $old) @unlink($old); echo json_encode([ 'success' => true, 'name' => basename($zipFile), 'size' => filesize($zipFile), 'created' => date('Y-m-d H:i:s'), ]); break; case 'download': $name = basename($_GET['file'] ?? ''); if (!preg_match('/^ttg_backup_[\d_-]+\.zip$/', $name)) { http_response_code(400); echo 'Invalid filename'; exit; } $path = $backupDir . '/' . $name; if (!file_exists($path)) { http_response_code(404); echo 'Not found'; exit; } header('Content-Type: application/zip'); header('Content-Disposition: attachment; filename="' . $name . '"'); header('Content-Length: ' . filesize($path)); header('Cache-Control: no-cache'); readfile($path); exit; case 'delete': if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false,'error'=>'POST required']); exit; } $data = json_decode(file_get_contents('php://input'), true); $name = basename($data['name'] ?? ''); if (!preg_match('/^ttg_backup_[\d_-]+\.zip$/', $name)) { echo json_encode(['success'=>false,'error'=>'Invalid filename']); exit; } $path = $backupDir . '/' . $name; if (file_exists($path)) @unlink($path); echo json_encode(['success'=>true]); break; default: echo json_encode(['success'=>false,'error'=>'Unknown action']); }