From 209e17fcd40168e8128fe9b4b5fca80d97837d28 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Fri, 20 Mar 2026 09:21:02 -0700 Subject: [PATCH] add https support --- README.md | 5 +++++ config.example.json | 4 ++++ server.js | 39 ++++++++++++++++++++++++++++++++++----- 3 files changed, 43 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 8695e8a6..3d3819bd 100644 --- a/README.md +++ b/README.md @@ -148,6 +148,10 @@ Edit `config.json`: ```json { "port": 3000, + "https": { + "cert": "/path/to/cert.pem", + "key": "/path/to/key.pem" + }, "mqtt": { "broker": "mqtt://localhost:1883", "topic": "meshcore/+/+/packets" @@ -178,6 +182,7 @@ Edit `config.json`: | Field | Description | |-------|-------------| | `port` | HTTP server port (default: 3000) | +| `https.cert` / `https.key` | Optional PEM cert/key paths to enable native HTTPS (falls back to HTTP if omitted or unreadable) | | `mqtt.broker` | Local MQTT broker URL. Set to `""` to disable | | `mqtt.topic` | MQTT topic pattern for packet ingestion | | `mqttSources` | Array of external MQTT broker connections (optional) | diff --git a/config.example.json b/config.example.json index cbd82f9c..b0dcdafa 100644 --- a/config.example.json +++ b/config.example.json @@ -1,5 +1,9 @@ { "port": 3000, + "https": { + "cert": "/path/to/cert.pem", + "key": "/path/to/key.pem" + }, "mqtt": { "broker": "mqtt://localhost:1883", "topic": "meshcore/+/+/packets" diff --git a/server.js b/server.js index af46701d..1220dce1 100644 --- a/server.js +++ b/server.js @@ -2,9 +2,11 @@ const express = require('express'); const http = require('http'); +const https = require('https'); const { WebSocketServer } = require('ws'); const mqtt = require('mqtt'); const path = require('path'); +const fs = require('fs'); const config = require('./config.json'); const decoder = require('./decoder'); const crypto = require('crypto'); @@ -137,7 +139,29 @@ const cache = new TTLCache(); db.seed(); const app = express(); -const server = http.createServer(app); + +function createServer(app, cfg) { + const tls = cfg.https || {}; + if (!tls.cert || !tls.key) { + return { server: http.createServer(app), isHttps: false }; + } + + try { + const certPath = path.resolve(tls.cert); + const keyPath = path.resolve(tls.key); + const options = { + cert: fs.readFileSync(certPath), + key: fs.readFileSync(keyPath), + }; + console.log(`[https] enabled (cert: ${certPath}, key: ${keyPath})`); + return { server: https.createServer(options, app), isHttps: true }; + } catch (e) { + console.error(`[https] failed to load TLS cert/key, falling back to HTTP: ${e.message}`); + return { server: http.createServer(app), isHttps: false }; + } +} + +const { server, isHttps } = createServer(app, config); // --- Performance Instrumentation --- const perfStats = { @@ -2142,8 +2166,10 @@ app.get('/{*splat}', (req, res) => { }); // --- Start --- -server.listen(process.env.PORT || config.port, () => { - console.log(`MeshCore Analyzer running on http://localhost:${config.port}`); +const listenPort = process.env.PORT || config.port; +server.listen(listenPort, () => { + const protocol = isHttps ? 'https' : 'http'; + console.log(`MeshCore Analyzer running on ${protocol}://localhost:${listenPort}`); // Pre-warm expensive caches on startup setTimeout(() => { const t0 = Date.now(); @@ -2179,7 +2205,8 @@ server.listen(process.env.PORT || config.port, () => { console.log(`[pre-warm] subpaths: ${Date.now() - t0}ms`); // Pre-warm other heavy analytics endpoints via self-request - const port = process.env.PORT || config.port; + const port = listenPort; + const warmClient = isHttps ? https : http; const warmEndpoints = [ '/api/analytics/rf', '/api/analytics/topology', @@ -2195,7 +2222,9 @@ server.listen(process.env.PORT || config.port, () => { return; } const ep = warmEndpoints[warmed++]; - http.get(`http://127.0.0.1:${port}${ep}`, (res) => { + const requestOptions = { hostname: '127.0.0.1', port, path: ep }; + if (isHttps) requestOptions.rejectUnauthorized = false; + warmClient.get(requestOptions, (res) => { res.resume(); res.on('end', warmNext); }).on('error', warmNext);