setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } catch(Exception $e) { echo json_encode(['error'=>'DB: '.$e->getMessage()]); exit; } // Init table $pdo->exec("CREATE TABLE IF NOT EXISTS admin.vault_guard_log ( id SERIAL PRIMARY KEY, file_path TEXT, vault_path TEXT, issue_type VARCHAR(50), vault_size INT, live_size INT, action_taken VARCHAR(50), created_at TIMESTAMP DEFAULT NOW() )"); function vaultToLive($vaultFile) { // __opt__wevads-arsenal__public__api__sentinel-brain.php -> /opt/wevads-arsenal/public/api/sentinel-brain.php $name = basename($vaultFile, '.gold'); $name = preg_replace('/\.gold$/', '', $name); $path = str_replace('__', '/', $name); if ($path[0] !== '/') $path = '/' . $path; return $path; } function checkIntegrity() { $vault = VAULT_DIR; $results = ['checked'=>0, 'healthy'=>0, 'truncated'=>0, 'missing'=>0, 'restored'=>0, 'details'=>[]]; foreach(glob("$vault/*") as $vf) { if (is_dir($vf)) continue; $bn = basename($vf); if (strpos($bn, '.gold') !== false) { $livePath = vaultToLive(str_replace('.gold', '', $bn)); } else { $livePath = vaultToLive($bn); } if (!file_exists($livePath)) { $results['missing']++; $results['details'][] = ['file'=>$livePath, 'status'=>'MISSING', 'vault_size'=>filesize($vf)]; continue; } $results['checked']++; $vaultSize = filesize($vf); $liveSize = filesize($livePath); // Check for truncation (live is significantly smaller than vault) if ($liveSize < $vaultSize * 0.9) { $results['truncated']++; $results['details'][] = [ 'file'=>$livePath, 'status'=>'TRUNCATED', 'vault_size'=>$vaultSize, 'live_size'=>$liveSize, 'loss_pct'=>round((1 - $liveSize/$vaultSize) * 100, 1) ]; } // Check for PHP syntax errors in live file elseif (pathinfo($livePath, PATHINFO_EXTENSION) === 'php') { $check = shell_exec("php -l " . escapeshellarg($livePath) . " 2>&1"); if (strpos($check, 'No syntax errors') === false) { $results['truncated']++; $results['details'][] = [ 'file'=>$livePath, 'status'=>'SYNTAX_ERROR', 'vault_size'=>$vaultSize, 'live_size'=>$liveSize, 'error'=>trim($check) ]; } else { $results['healthy']++; } } else { $results['healthy']++; } } return $results; } function autoRestore($pdo) { $vault = VAULT_DIR; $fixed = 0; $details = []; foreach(glob("$vault/*") as $vf) { if (is_dir($vf)) continue; $bn = basename($vf); if (strpos($bn, '.gold') !== false) { $livePath = vaultToLive(str_replace('.gold', '', $bn)); } else { $livePath = vaultToLive($bn); } if (!file_exists($livePath)) continue; $vaultSize = filesize($vf); $liveSize = filesize($livePath); $needRestore = false; $issue = ''; // Truncation check if ($liveSize < $vaultSize * 0.9) { $needRestore = true; $issue = 'truncated'; } // PHP syntax check elseif (pathinfo($livePath, PATHINFO_EXTENSION) === 'php') { $check = shell_exec("php -l " . escapeshellarg($livePath) . " 2>&1"); if (strpos($check, 'No syntax errors') === false) { $needRestore = true; $issue = 'syntax_error'; } } if ($needRestore) { // Backup broken file copy($livePath, $livePath . '.broken_' . time()); // Restore from vault copy($vf, $livePath); // Verify fix $newCheck = shell_exec("php -l " . escapeshellarg($livePath) . " 2>&1"); $success = strpos($newCheck, 'No syntax errors') !== false || pathinfo($livePath, PATHINFO_EXTENSION) !== 'php'; if ($success) { $fixed++; $details[] = ['file'=>$livePath, 'issue'=>$issue, 'action'=>'RESTORED', 'vault_size'=>$vaultSize, 'was_size'=>$liveSize]; // Log to DB $pdo->prepare("INSERT INTO admin.vault_guard_log(file_path,vault_path,issue_type,vault_size,live_size,action_taken) VALUES(?,?,?,?,?,?)") ->execute([$livePath, $vf, $issue, $vaultSize, $liveSize, 'auto_restored']); // Log to file file_put_contents(LOG_FILE, date('Y-m-d H:i:s') . " RESTORED $livePath ($issue: $liveSize→$vaultSize)\n", FILE_APPEND); } } } return ['restored'=>$fixed, 'details'=>$details]; } function snapshotToVault() { // Snapshot critical files TO vault (for files not yet vaulted) $critical = [ '/opt/wevads-arsenal/public/api/sentinel-brain.php', '/opt/wevads-arsenal/public/api/hamid-engine.php', '/opt/wevads-arsenal/public/api/hamid-chef.php', '/opt/wevads-arsenal/public/api/hamid-ia.php', '/opt/wevads-arsenal/public/api/weval-mind-core.php', '/opt/wevads-arsenal/public/api/brain-unified-send.php', '/opt/wevads-arsenal/public/api/warmup-engine.php', '/opt/wevads-arsenal/public/command-center.html', '/opt/wevads/public/js/brain-inject.js', '/opt/wevads/public/brain-inject-api.php', '/opt/wevads/public/brain-unified-send.php', ]; $saved = 0; foreach($critical as $f) { if (!file_exists($f)) continue; // Only snapshot if syntax is OK if (pathinfo($f, PATHINFO_EXTENSION) === 'php') { $check = shell_exec("php -l " . escapeshellarg($f) . " 2>&1"); if (strpos($check, 'No syntax errors') === false) continue; } $vaultName = str_replace('/', '__', ltrim($f, '/')); $dest = VAULT_DIR . '/' . $vaultName; // Only overwrite if live is >= vault size (no truncation) if (file_exists($dest) && filesize($f) < filesize($dest) * 0.9) continue; copy($f, $dest); $saved++; } return ['snapshot_count'=>$saved]; } function getStatus($pdo) { $lastRestore = $pdo->query("SELECT * FROM admin.vault_guard_log ORDER BY created_at DESC LIMIT 5")->fetchAll(PDO::FETCH_ASSOC); $totalRestores = $pdo->query("SELECT COUNT(*) FROM admin.vault_guard_log")->fetchColumn(); $vaultFiles = count(glob(VAULT_DIR . '/*')); $check = checkIntegrity(); return [ 'status'=>$check['truncated']===0 ? 'healthy' : 'issues_found', 'vault_files'=>$vaultFiles, 'checked'=>$check['checked'], 'healthy'=>$check['healthy'], 'truncated'=>$check['truncated'], 'missing'=>$check['missing'], 'total_restores'=>(int)$totalRestores, 'last_restores'=>$lastRestore, 'issues'=>$check['details'] ]; } // Router $action = $_GET['action'] ?? 'status'; switch($action) { case 'check': echo json_encode(checkIntegrity(), JSON_PRETTY_PRINT); break; case 'restore': echo json_encode(autoRestore($pdo), JSON_PRETTY_PRINT); break; case 'snapshot': echo json_encode(snapshotToVault(), JSON_PRETTY_PRINT); break; case 'cron': // Full cycle: check → restore → snapshot $check = checkIntegrity(); $restore = ['restored'=>0]; if ($check['truncated'] > 0) { $restore = autoRestore($pdo); } $snap = snapshotToVault(); echo json_encode([ 'cycle'=>'complete', 'checked'=>$check['checked'], 'truncated'=>$check['truncated'], 'restored'=>$restore['restored'], 'snapshot'=>$snap['snapshot_count'], 'timestamp'=>date('Y-m-d H:i:s') ]); break; case 'status': default: echo json_encode(getStatus($pdo), JSON_PRETTY_PRINT); break; }