/** * Optional agent-style HTTP API bound to the PearMonitor agent. * * Default: 127.0.0.1:18888 — local only. * Disable with PEARDATA_REST=0 */ import http from 'http' import b4a from 'b4a' import { handleRest } from './routes.js' import logger from '../utils/logger.js' const log = logger.child('rest') /** * @returns {http.Server|null} */ export function startRestServer() { if (process.env.PEARDATA_REST === '0' || process.env.PEARDATA_REST === 'off') { log.info('REST API disabled (PEARDATA_REST=0)') return null } const host = process.env.PEARDATA_REST_HOST || '127.0.0.1' const port = Number(process.env.PEARDATA_REST_PORT) || 18888 const server = http.createServer(async (req, res) => { try { const url = new URL(req.url || '/', `http://${host}:${port}`) if (req.method !== 'GET' && req.method !== 'HEAD' && req.method !== 'OPTIONS') { res.writeHead(405, { 'content-type': 'application/json', allow: 'GET, HEAD, OPTIONS' }) res.end(JSON.stringify({ error: 'method not allowed' })) return } if (req.method === 'OPTIONS') { res.writeHead(204, corsHeaders()) res.end() return } const result = await handleRest(url.pathname, url.searchParams) const headers = { ...corsHeaders(), 'content-type': result.contentType || 'application/json', 'cache-control': 'no-cache', 'x-peardata-api': 'v3', } const body = typeof result.body === 'string' ? result.body : JSON.stringify(result.body, null, 0) if (req.method === 'HEAD') { headers['content-length'] = b4a.byteLength(body) res.writeHead(result.status || 200, headers) res.end() return } res.writeHead(result.status || 200, headers) res.end(body) } catch (err) { log.error('REST handler error', { error: err.message }) res.writeHead(500, { 'content-type': 'application/json' }) res.end(JSON.stringify({ error: 'internal error' })) } }) server.listen(port, host, () => { log.info('REST API listening', { url: `http://${host}:${port}`, examples: [ `http://${host}:${port}/api/v3/info`, `http://${host}:${port}/api/v3/data?chart=system.cpu&after=-60&points=60`, `http://${host}:${port}/api/v1/charts`, ], }) }) server.on('error', (err) => { log.error('REST server error', { error: err.message }) }) return server } function corsHeaders() { return { 'access-control-allow-origin': process.env.PEARDATA_REST_CORS || '*', 'access-control-allow-methods': 'GET, HEAD, OPTIONS', 'access-control-allow-headers': 'Content-Type, Authorization', } }