feat: add new Flowme and FlowmeChannel management with CRUD operations and UI components
This commit is contained in:
214
public/sse-test.html
Normal file
214
public/sse-test.html
Normal file
@@ -0,0 +1,214 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>SSE 测试</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
max-width: 800px;
|
||||
margin: 50px auto;
|
||||
padding: 20px;
|
||||
}
|
||||
.status {
|
||||
padding: 10px;
|
||||
margin: 10px 0;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.status.connected { background: #d4edda; color: #155724; }
|
||||
.status.connecting { background: #fff3cd; color: #856404; }
|
||||
.status.disconnected { background: #f8d7da; color: #721c24; }
|
||||
.events {
|
||||
border: 1px solid #ddd;
|
||||
padding: 10px;
|
||||
height: 400px;
|
||||
overflow-y: auto;
|
||||
background: #f9f9f9;
|
||||
}
|
||||
.event-item {
|
||||
padding: 8px;
|
||||
margin: 5px 0;
|
||||
background: #fff;
|
||||
border: 1px solid #eee;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.event-item .time {
|
||||
color: #888;
|
||||
font-size: 12px;
|
||||
}
|
||||
.event-item .data {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>SSE / HTTP Stream 测试</h1>
|
||||
<div>
|
||||
<label>URL: </label>
|
||||
<input type="text" id="url" value="http://localhost:4005/root/v3/" style="width: 400px;">
|
||||
<label>类型: </label>
|
||||
<select id="streamType">
|
||||
<option value="sse">SSE (EventSource)</option>
|
||||
<option value="stream">HTTP Stream (Fetch)</option>
|
||||
</select>
|
||||
<button onclick="connect()">连接</button>
|
||||
<button onclick="disconnect()">断开</button>
|
||||
</div>
|
||||
<div id="status" class="status disconnected">未连接</div>
|
||||
<h3>事件日志</h3>
|
||||
<div id="events" class="events"></div>
|
||||
|
||||
<script>
|
||||
let eventSource = null;
|
||||
let abortController = null;
|
||||
|
||||
async function connect() {
|
||||
const url = document.getElementById('url').value;
|
||||
const type = document.getElementById('streamType').value;
|
||||
|
||||
if (!url) {
|
||||
alert('请输入 URL');
|
||||
return;
|
||||
}
|
||||
|
||||
disconnect();
|
||||
const eventsDiv = document.getElementById('events');
|
||||
eventsDiv.innerHTML = '';
|
||||
updateStatus('connecting', '正在连接...');
|
||||
|
||||
if (type === 'sse') {
|
||||
connectSSE(url);
|
||||
} else {
|
||||
connectStream(url);
|
||||
}
|
||||
}
|
||||
|
||||
function connectSSE(url) {
|
||||
try {
|
||||
eventSource = new EventSource(url);
|
||||
|
||||
eventSource.onopen = function() {
|
||||
updateStatus('connected', '已连接 (SSE)');
|
||||
addEvent('系统', 'SSE 连接已建立');
|
||||
};
|
||||
|
||||
eventSource.onmessage = function(event) {
|
||||
addEvent('消息', event.data, 'message');
|
||||
};
|
||||
|
||||
eventSource.onerror = function(error) {
|
||||
// 如果 readyState 是 CLOSED,说明是后端主动关闭,不重连
|
||||
if (eventSource.readyState === EventSource.CLOSED) {
|
||||
updateStatus('disconnected', '连接已关闭');
|
||||
addEvent('系统', '后端已关闭连接,无须重连');
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果是其他错误状态,显示错误信息,不自动重连
|
||||
updateStatus('disconnected', '连接错误');
|
||||
addEvent('错误', 'SSE 连接发生错误');
|
||||
};
|
||||
|
||||
eventSource.addEventListener('data', function(event) {
|
||||
addEvent('自定义事件', event.data, 'data');
|
||||
});
|
||||
|
||||
} catch (e) {
|
||||
updateStatus('disconnected', '连接失败: ' + e.message);
|
||||
addEvent('错误', e.message);
|
||||
}
|
||||
}
|
||||
|
||||
async function connectStream(url) {
|
||||
try {
|
||||
abortController = new AbortController();
|
||||
updateStatus('connected', '已连接 (Stream)');
|
||||
addEvent('系统', 'HTTP Stream 连接已建立');
|
||||
|
||||
const response = await fetch(url, {
|
||||
signal: abortController.signal,
|
||||
headers: {
|
||||
'Accept': 'text/plain, text/event-stream'
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const reader = response.body.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
let buffer = '';
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) {
|
||||
addEvent('系统', 'Stream 连接已关闭');
|
||||
break;
|
||||
}
|
||||
|
||||
// 解码并处理数据
|
||||
buffer += decoder.decode(value, { stream: true });
|
||||
|
||||
// 按行处理
|
||||
const lines = buffer.split('\n');
|
||||
buffer = lines.pop() || '';
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.trim()) {
|
||||
addEvent('数据', line, 'stream');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
if (e.name !== 'AbortError') {
|
||||
updateStatus('disconnected', '连接错误: ' + e.message);
|
||||
addEvent('错误', e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function disconnect() {
|
||||
if (eventSource) {
|
||||
eventSource.close();
|
||||
eventSource = null;
|
||||
}
|
||||
if (abortController) {
|
||||
abortController.abort();
|
||||
abortController = null;
|
||||
}
|
||||
updateStatus('disconnected', '已断开');
|
||||
addEvent('系统', '连接已关闭');
|
||||
}
|
||||
|
||||
function updateStatus(type, message) {
|
||||
const statusDiv = document.getElementById('status');
|
||||
statusDiv.className = 'status ' + type;
|
||||
statusDiv.textContent = message;
|
||||
}
|
||||
|
||||
function addEvent(type, data, eventType = '') {
|
||||
const eventsDiv = document.getElementById('events');
|
||||
const item = document.createElement('div');
|
||||
item.className = 'event-item';
|
||||
|
||||
const time = new Date().toLocaleTimeString();
|
||||
item.innerHTML = `
|
||||
<div class="time">[${time}] ${eventType ? '[' + eventType + '] ' : ''}${type}</div>
|
||||
<div class="data">${escapeHtml(data)}</div>
|
||||
`;
|
||||
|
||||
eventsDiv.insertBefore(item, eventsDiv.firstChild);
|
||||
}
|
||||
|
||||
function escapeHtml(text) {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = text;
|
||||
return div.innerHTML;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user