diff --git a/server/status.js b/server/status.js index 5d92d1a..49e0f1e 100644 --- a/server/status.js +++ b/server/status.js @@ -4,147 +4,220 @@ const net = require('net'); const port = 8080; const fulcrumTimeoutMs = 3000; -function checkFulcrum(fulcrumUrl) { - return new Promise((resolve) => { - if (!fulcrumUrl) { - resolve({ - configured: false, - connected: false, - url: null, - }); - return; - } +function queryFulcrum(fulcrumUrl, method, params = []) { +return new Promise((resolve) => { +if (!fulcrumUrl) { +resolve({ +ok: false, +result: null, +}); +return; +} - let parsed; +``` +let parsed; + +try { + parsed = new URL(fulcrumUrl); +} catch { + resolve({ + ok: false, + result: null, + }); + return; +} + +if (parsed.protocol !== 'tcp:') { + resolve({ + ok: false, + result: null, + }); + return; +} + +const host = parsed.hostname; +const fulcrumPort = Number(parsed.port); + +if (!host || !fulcrumPort) { + resolve({ + ok: false, + result: null, + }); + return; +} + +const socket = net.createConnection({ + host, + port: fulcrumPort, +}); + +let response = ''; +let settled = false; + +const finish = (value) => { + if (settled) return; + settled = true; + + socket.destroy(); + resolve(value); +}; + +socket.setTimeout(fulcrumTimeoutMs); + +socket.on('connect', () => { + socket.write( + `${JSON.stringify({ + jsonrpc: '2.0', + id: 1, + method, + params, + })}\n`, + ); +}); + +socket.on('data', (data) => { + response += data.toString(); + + const lines = response.split('\n'); + + for (const line of lines) { + if (!line.trim()) continue; + + let message; try { - parsed = new URL(fulcrumUrl); + message = JSON.parse(line); } catch { - resolve({ - configured: true, - connected: false, - url: fulcrumUrl, + continue; + } + + if (message.error) { + finish({ + ok: false, + result: null, }); return; } - if (parsed.protocol !== 'tcp:') { - resolve({ - configured: true, - connected: false, - url: fulcrumUrl, + if (Object.prototype.hasOwnProperty.call(message, 'result')) { + finish({ + ok: true, + result: message.result, }); return; } + } +}); - const host = parsed.hostname; - const port = Number(parsed.port); - - if (!host || !port) { - resolve({ - configured: true, - connected: false, - url: fulcrumUrl, - }); - return; - } - - const socket = net.createConnection({ - host, - port, - }); - - let response = ''; - let settled = false; - - const finish = (connected) => { - if (settled) return; - settled = true; - - socket.destroy(); - - resolve({ - configured: true, - connected, - url: fulcrumUrl, - }); - }; - - socket.setTimeout(fulcrumTimeoutMs); - - socket.on('connect', () => { - socket.write( - `${JSON.stringify({ - jsonrpc: '2.0', - id: 1, - method: 'server.version', - params: ['Munin Bitcoin', '1.4'], - })}\n`, - ); - }); - - socket.on('data', (data) => { - response += data.toString(); - - if (response.includes('"result"')) { - finish(true); - } else if (response.includes('"error"')) { - finish(false); - } - }); - - socket.on('timeout', () => finish(false)); - socket.on('error', () => finish(false)); - socket.on('close', () => finish(false)); +socket.on('timeout', () => { + finish({ + ok: false, + result: null, }); +}); + +socket.on('error', () => { + finish({ + ok: false, + result: null, + }); +}); + +socket.on('close', () => { + finish({ + ok: false, + result: null, + }); +}); +``` + +}); +} + +async function getFulcrumStatus(fulcrumUrl) { +const version = await queryFulcrum( +fulcrumUrl, +'server.version', +['Munin Bitcoin', '1.4'], +); + +if (!version.ok) { +return { +configured: Boolean(fulcrumUrl), +connected: false, +url: fulcrumUrl, +serverVersion: null, +blockchainHeight: null, +}; +} + +const headers = await queryFulcrum( +fulcrumUrl, +'blockchain.headers.subscribe', +[], +); + +return { +configured: Boolean(fulcrumUrl), +connected: true, +url: fulcrumUrl, +serverVersion: version.result, +blockchainHeight: +headers.ok && headers.result +? headers.result.height ?? null +: null, +}; } async function getStatus() { - const fulcrumUrl = process.env.MUNIN_FULCRUM_URL || null; - const fulcrum = await checkFulcrum(fulcrumUrl); +const fulcrumUrl = process.env.MUNIN_FULCRUM_URL || null; +const fulcrum = await getFulcrumStatus(fulcrumUrl); - return { - service: 'running', - fulcrum, - wallet: 'not configured', - labels: 0, - transactions: 0, - lastScan: 'never', - }; +return { +service: 'running', +fulcrum, +wallet: 'not configured', +labels: 0, +transactions: 0, +lastScan: 'never', +}; } const server = http.createServer(async (req, res) => { - if (req.url === '/api/status') { - try { - const status = await getStatus(); +if (req.url === '/api/status') { +try { +const status = await getStatus(); - res.writeHead(200, { - 'Content-Type': 'application/json', - }); +``` + res.writeHead(200, { + 'Content-Type': 'application/json', + }); - res.end(JSON.stringify(status)); - } catch (error) { - console.error('Failed to get service status:', error); + res.end(JSON.stringify(status)); +} catch (error) { + console.error('Failed to get service status:', error); - res.writeHead(500, { - 'Content-Type': 'application/json', - }); + res.writeHead(500, { + 'Content-Type': 'application/json', + }); - res.end( - JSON.stringify({ - service: 'running', - error: 'Failed to determine service status', - }), - ); - } + res.end( + JSON.stringify({ + service: 'running', + error: 'Failed to determine service status', + }), + ); +} - return; - } +return; +``` - res.writeHead(404); - res.end('Not found'); +} + +res.writeHead(404); +res.end('Not found'); }); server.listen(port, () => { - console.log(`Status API listening on port ${port}`); -}); \ No newline at end of file +console.log(`Status API listening on port ${port}`); +});