fix: use native EventSource for SSE instead of fetch with manual parsing
This commit is contained in:
+17
-50
@@ -56,62 +56,33 @@ async function refreshMaps() {
|
||||
}
|
||||
|
||||
// ── SSE streams per chain ───────────────────────────────────────────
|
||||
const activeStreams = new Map<string, AbortController>();
|
||||
const activeStreams = new Map<string, EventSource>();
|
||||
|
||||
/** Start a raw SSE stream for a chain — receives ALL txs and blocks. */
|
||||
function startChainStream(chain: string) {
|
||||
if (activeStreams.has(chain)) return;
|
||||
|
||||
const ac = new AbortController();
|
||||
activeStreams.set(chain, ac);
|
||||
|
||||
const query = { crypto: chain };
|
||||
const q = Buffer.from(JSON.stringify(query)).toString("base64");
|
||||
const url = `${SOCK_API}/sse?q=${q}`;
|
||||
|
||||
connectSSE(chain, url, ac.signal);
|
||||
}
|
||||
const es = new EventSource(url);
|
||||
activeStreams.set(chain, es);
|
||||
|
||||
async function connectSSE(chain: string, url: string, signal: AbortSignal) {
|
||||
while (!signal.aborted) {
|
||||
es.onmessage = (e) => {
|
||||
try {
|
||||
const res = await fetch(url, { signal });
|
||||
if (!res.ok || !res.body) {
|
||||
console.error(`SSE ${chain}: HTTP ${res.status}`);
|
||||
await sleep(5000);
|
||||
continue;
|
||||
const event = JSON.parse(e.data);
|
||||
if (event.type === "block") {
|
||||
handleBlockEvent(chain, event).catch(() => {});
|
||||
} else {
|
||||
handleTxEvent(chain, event).catch(() => {});
|
||||
}
|
||||
} catch {}
|
||||
};
|
||||
|
||||
const reader = res.body.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
let buffer = "";
|
||||
|
||||
while (!signal.aborted) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
|
||||
buffer += decoder.decode(value, { stream: true });
|
||||
const lines = buffer.split("\n");
|
||||
buffer = lines.pop() ?? "";
|
||||
|
||||
for (const line of lines) {
|
||||
if (!line.startsWith("data: ")) continue;
|
||||
try {
|
||||
const event = JSON.parse(line.slice(6));
|
||||
if (event.type === "block") {
|
||||
await handleBlockEvent(chain, event);
|
||||
} else {
|
||||
await handleTxEvent(chain, event);
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
} catch (e: any) {
|
||||
if (signal.aborted) return;
|
||||
console.error(`SSE ${chain} error:`, e.message);
|
||||
}
|
||||
if (!signal.aborted) await sleep(3000);
|
||||
}
|
||||
es.onerror = () => {
|
||||
console.error(`SSE ${chain}: connection error (will auto-reconnect)`);
|
||||
};
|
||||
}
|
||||
|
||||
/** Handle a tx event — compare all outputs against our active addresses. */
|
||||
@@ -237,9 +208,9 @@ async function syncStreams() {
|
||||
if (COINS[chain]) startChainStream(chain);
|
||||
}
|
||||
|
||||
for (const [chain, ac] of activeStreams) {
|
||||
for (const [chain, es] of activeStreams) {
|
||||
if (!chainsNeeded.has(chain)) {
|
||||
ac.abort();
|
||||
es.close();
|
||||
activeStreams.delete(chain);
|
||||
}
|
||||
}
|
||||
@@ -365,8 +336,4 @@ export async function expireProPlans() {
|
||||
if (result.count > 0) {
|
||||
console.log(`Downgraded ${result.count} expired pro accounts to free`);
|
||||
}
|
||||
}
|
||||
|
||||
function sleep(ms: number) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user