201 lines
7.1 KiB
PHP
201 lines
7.1 KiB
PHP
<?php
|
|
require 'config.php';
|
|
|
|
function callApi($url) {
|
|
$ch = curl_init();
|
|
curl_setopt($ch, CURLOPT_URL, $url);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); // 10秒连接超时
|
|
curl_setopt($ch, CURLOPT_TIMEOUT, 30); // 30秒执行超时
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
|
'Content-Type: application/json',
|
|
'Accept: application/json'
|
|
]);
|
|
// 禁用SSL验证
|
|
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
|
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
|
|
$response = curl_exec($ch);
|
|
if (curl_errno($ch)) {
|
|
$error_msg = curl_error($ch);
|
|
curl_close($ch);
|
|
return json_encode(['error' => $error_msg]);
|
|
}
|
|
curl_close($ch);
|
|
return $response;
|
|
}
|
|
|
|
if (isset($_SERVER['HTTP_ACCEPT']) && strpos($_SERVER['HTTP_ACCEPT'], 'text/event-stream') !== false) {
|
|
// 设置SSE头信息
|
|
header('Content-Type: text/event-stream; charset=UTF-8');
|
|
header('Cache-Control: no-cache, must-revalidate');
|
|
header('Connection: keep-alive');
|
|
header('X-Accel-Buffering: no'); // 禁用Nginx缓冲
|
|
header('Access-Control-Allow-Origin: *');
|
|
header('Content-Encoding: none'); // 禁用压缩
|
|
|
|
// 设置脚本超时时间
|
|
set_time_limit(0);
|
|
ignore_user_abort(true);
|
|
|
|
// 禁用输出缓冲
|
|
if (ob_get_level()) {
|
|
ob_end_clean();
|
|
}
|
|
ob_implicit_flush(true);
|
|
|
|
// 发送心跳包保持连接
|
|
function sendHeartbeat() {
|
|
echo ": heartbeat\n\n";
|
|
ob_flush();
|
|
flush();
|
|
}
|
|
|
|
$total = count($links);
|
|
$completed = 0;
|
|
|
|
// 发送初始心跳
|
|
sendHeartbeat();
|
|
|
|
// 检查连接是否中断
|
|
if (connection_aborted()) {
|
|
exit;
|
|
}
|
|
|
|
foreach ($links as $link) {
|
|
$startTime = microtime(true);
|
|
$result = callApi($link);
|
|
$endTime = microtime(true);
|
|
$executionTime = round(($endTime - $startTime) * 1000, 2); // 毫秒
|
|
|
|
$completed++;
|
|
$progress = round(($completed / $total) * 100);
|
|
|
|
$data = [
|
|
'link' => $link,
|
|
'start_time' => date('Y-m-d H:i:s', $startTime),
|
|
'end_time' => date('Y-m-d H:i:s', $endTime),
|
|
'execution_time' => $executionTime,
|
|
'result' => $result,
|
|
'progress' => $progress
|
|
];
|
|
|
|
echo "data: " . json_encode($data) . "\n\n";
|
|
ob_flush();
|
|
flush();
|
|
}
|
|
|
|
echo "event: complete\ndata: {}\n\n";
|
|
exit;
|
|
}
|
|
?>
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>API 调用工具</title>
|
|
<style>
|
|
body { font-family: Arial, sans-serif; padding: 20px; }
|
|
#results { margin-top: 20px; white-space: pre-wrap; }
|
|
.result-block { margin-bottom: 20px; padding: 10px; border: 1px solid #ddd; }
|
|
.progress-container {
|
|
width: 100%;
|
|
background-color: #f3f3f3;
|
|
margin: 20px 0;
|
|
border-radius: 5px;
|
|
overflow: hidden;
|
|
}
|
|
.progress-bar {
|
|
width: 0;
|
|
height: 30px;
|
|
background-color: #4caf50;
|
|
text-align: center;
|
|
line-height: 30px;
|
|
color: white;
|
|
transition: width 0.3s ease;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h1>API 调用工具</h1>
|
|
<button id="executeBtn">执行 API 调用</button>
|
|
<div class="progress-container">
|
|
<div id="progressBar" class="progress-bar">0%</div>
|
|
</div>
|
|
<div id="results"></div>
|
|
|
|
<script>
|
|
document.getElementById('executeBtn').addEventListener('click', function() {
|
|
const resultsDiv = document.getElementById('results');
|
|
resultsDiv.innerHTML = '<div id="result-container"></div>';
|
|
|
|
fetch(window.location.href, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
}
|
|
}).then(() => {
|
|
const eventSource = new EventSource(window.location.href);
|
|
const resultContainer = document.getElementById('result-container');
|
|
|
|
eventSource.onmessage = function(event) {
|
|
const data = JSON.parse(event.data);
|
|
|
|
// 更新进度条
|
|
const progressBar = document.getElementById('progressBar');
|
|
progressBar.style.width = data.progress + '%';
|
|
progressBar.textContent = data.progress + '%';
|
|
|
|
// 添加新结果
|
|
const resultBlock = document.createElement('div');
|
|
resultBlock.className = 'result-block';
|
|
resultBlock.innerHTML = `
|
|
<strong>访问链接:</strong> ${data.link}<br>
|
|
<strong>开始时间:</strong> ${data.start_time}<br>
|
|
<strong>结束时间:</strong> ${data.end_time}<br>
|
|
<strong>执行时间:</strong> ${data.execution_time}ms<br>
|
|
<strong>返回结果:</strong> ${data.result}
|
|
`;
|
|
resultContainer.appendChild(resultBlock);
|
|
};
|
|
|
|
eventSource.addEventListener('complete', function() {
|
|
eventSource.close();
|
|
progressBar.style.backgroundColor = '#4caf50';
|
|
progressBar.textContent = '完成';
|
|
});
|
|
|
|
let reconnectAttempts = 0;
|
|
const maxReconnectAttempts = 3;
|
|
|
|
eventSource.onerror = function(e) {
|
|
if (e.eventPhase === EventSource.CLOSED) {
|
|
// 服务器主动关闭连接
|
|
eventSource.close();
|
|
if (progressBar.textContent !== '完成') {
|
|
resultsDiv.innerHTML = '连接已关闭';
|
|
}
|
|
} else {
|
|
// 连接中断,尝试重连
|
|
if (reconnectAttempts < maxReconnectAttempts) {
|
|
reconnectAttempts++;
|
|
setTimeout(function() {
|
|
eventSource = new EventSource('');
|
|
}, 1000 * reconnectAttempts); // 指数退避
|
|
} else {
|
|
eventSource.close();
|
|
resultsDiv.innerHTML = '连接中断,请重试';
|
|
}
|
|
}
|
|
};
|
|
|
|
// 添加心跳检测
|
|
const heartbeatInterval = setInterval(() => {
|
|
if (eventSource.readyState === EventSource.CLOSED) {
|
|
clearInterval(heartbeatInterval);
|
|
}
|
|
}, 5000);
|
|
});
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|