90 lines
2.7 KiB
JavaScript
90 lines
2.7 KiB
JavaScript
/**
|
|
* 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',
|
|
}
|
|
}
|