false, 'err'=>'not found']; $content = file_get_contents($file); // Skip if already patched if (strpos($content, 'DOCTRINE-141-SHUTDOWN') !== false || strpos($content, 'DOCTRINE-141-INLINE') !== false) { return ['ok'=>false, 'err'=>'already patched']; } // Check chattr $attr = shell_exec("lsattr $file 2>&1"); if (strpos($attr, 'i') !== false && strpos($attr, '----i') !== false) { shell_exec("sudo chattr -i $file 2>/dev/null"); } // Backup $backup = '/var/www/html/vault-gold/opus/' . basename($file) . '.doctrine141-' . date('Ymd-His') . '.bak'; @mkdir(dirname($backup), 0755, true); copy($file, $backup); // === Bloc à injecter (même que v1) === $inject = "\n// === DOCTRINE-141-SHUTDOWN · memory bridge === if (@file_exists(__DIR__.'/wevia-memory-bridge.php')) { @require_once __DIR__.'/wevia-memory-bridge.php'; } \$__chat_id = '" . $chat_id . "'; \$__user_id = \$_COOKIE['weval_chat_session'] ?? \$_SERVER['HTTP_X_USER_ID'] ?? ('anon-'.substr(md5((\$_SERVER['REMOTE_ADDR']??'').(\$_SERVER['HTTP_USER_AGENT']??'')),0,12)); \$__msg_for_mem = ''; register_shutdown_function(function() use (&\$__msg_for_mem, \$__chat_id, \$__user_id) { if (!function_exists('wevia_mem_save')) return; \$out = ob_get_contents(); if (!\$out) return; \$d = @json_decode(\$out, true); \$resp = is_array(\$d) ? (\$d['response'] ?? \$d['answer'] ?? \$d['content'] ?? '') : \$out; if (\$resp && \$__msg_for_mem) { @wevia_mem_save(\$__chat_id, \$__user_id, \$__msg_for_mem, is_string(\$resp)?\$resp:json_encode(\$resp), 'internal'); } }); ob_start(); // === /DOCTRINE-141-SHUTDOWN === \n"; // === Anchors étendus (v2) === $anchors = [ // v1 anchors (doubles quotes) '$input = json_decode(file_get_contents("php://input"), true) ?: [];', '$input = @json_decode(file_get_contents("php://input"), true) ?: [];', '$raw = @file_get_contents("php://input");', // v2 NEW anchors (simples quotes) "\$input=json_decode(file_get_contents('php://input'),true);", "\$input = json_decode(file_get_contents('php://input'), true) ?: [];", "\$input = json_decode(file_get_contents('php://input'), true);", "\$input = @json_decode(file_get_contents('php://input'), true) ?: [];", ]; $injected = false; foreach ($anchors as $anchor) { $pos = strpos($content, $anchor); if ($pos !== false) { $end = strpos($content, "\n", $pos) + 1; $before = substr($content, 0, $end); $after = substr($content, $end); $content = $before . $inject . $after; $injected = true; break; } } if (!$injected) { shell_exec("sudo chattr +i $file 2>/dev/null"); return ['ok'=>false, 'err'=>'no anchor found']; } // Msg captures (v1 + v2 extended) $msg_captures = array_merge([ ['old' => '$msg = $input["message"] ?? $_GET["message"] ?? "";', 'new' => '$msg = $input["message"] ?? $_GET["message"] ?? ""; $__msg_for_mem = $msg;'], ['old' => '$msg = trim($in["message"] ?? "");', 'new' => '$msg = trim($in["message"] ?? ""); $__msg_for_mem = $msg;'], // v2: saas-chat style (simples quotes) ['old' => "\$msg=\$input['message']??\$_POST['message']??'';", 'new' => "\$msg=\$input['message']??\$_POST['message']??''; \$__msg_for_mem = \$msg;"], // v2: claude-pattern style ['old' => "\$message = trim(\$input['message'] ?? '');", 'new' => "\$message = trim(\$input['message'] ?? ''); \$__msg_for_mem = \$message;"], ], $extra_msg_captures); $capture_ok = false; foreach ($msg_captures as $cap) { if (strpos($content, $cap['old']) !== false) { $content = str_replace($cap['old'], $cap['new'], $content); $capture_ok = true; break; } } // Lint $tmp = tempnam('/tmp', 'cbv2-'); file_put_contents($tmp, $content); $lint = shell_exec("php -l $tmp 2>&1"); if (strpos($lint, 'No syntax errors') === false) { unlink($tmp); shell_exec("sudo chattr +i $file 2>/dev/null"); return ['ok'=>false, 'err'=>'lint fail', 'lint'=>$lint]; } // Write file_put_contents($file, $content); shell_exec("sudo chown www-data:www-data $file"); shell_exec("sudo chattr +i $file 2>/dev/null"); unlink($tmp); return ['ok'=>true, 'backup'=>$backup, 'chat_id'=>$chat_id, 'capture_applied'=>$capture_ok]; } $results = [ 'saas-chat' => patch_chatbot_v2('/var/www/html/api/saas-chat.php', 'saas-chat'), 'claude-pattern-api' => patch_chatbot_v2('/var/www/html/api/claude-pattern-api.php', 'claude-pattern-api'), ]; @opcache_reset(); // Tests sanity foreach (['saas-chat', 'claude-pattern-api'] as $name) { if ($results[$name]['ok'] ?? false) { $sess = 'opus-ph6-' . $name; $test = shell_exec("curl -sk -m 8 'http://localhost/api/$name.php' -H 'Content-Type: application/json' -H 'X-User-Id: $sess' -d '{\"message\":\"hello test phase 6\"}' 2>&1 | head -c 200"); $results[$name]['test'] = trim($test); sleep(1); // Check Redis if (file_exists('/var/www/html/api/wevia-memory-bridge.php')) { require_once '/var/www/html/api/wevia-memory-bridge.php'; $hist = wevia_mem_load($name, $sess, 5); $results[$name]['redis_msgs'] = count($hist); } } } echo json_encode($results, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);