659 lines
18 KiB
JavaScript
659 lines
18 KiB
JavaScript
const http = require('http')
|
|
const fs = require('fs')
|
|
const path = require('path')
|
|
const WebSocket = require('ws')
|
|
const { WebSocketServer } = WebSocket
|
|
|
|
const HOST = '0.0.0.0'
|
|
const PORT = 17865
|
|
const GPS_WS_PATH = '/mock-gps'
|
|
const HEART_RATE_WS_PATH = '/mock-hr'
|
|
const DEBUG_LOG_WS_PATH = '/debug-log'
|
|
const PROXY_PATH = '/proxy'
|
|
const BRIDGE_STATUS_PATH = '/bridge-status'
|
|
const BRIDGE_CONFIG_PATH = '/bridge-config'
|
|
const PUBLIC_DIR = path.join(__dirname, 'public')
|
|
const DEFAULT_GATEWAY_BRIDGE_URL = 'ws://127.0.0.1:18080/ws'
|
|
|
|
const INITIAL_BRIDGE_CONFIG = {
|
|
enabled: process.env.MOCK_SIM_GATEWAY_ENABLED === '1',
|
|
url: process.env.MOCK_SIM_GATEWAY_URL || DEFAULT_GATEWAY_BRIDGE_URL,
|
|
token: process.env.MOCK_SIM_GATEWAY_TOKEN || 'dev-producer-token',
|
|
channelId: process.env.MOCK_SIM_GATEWAY_CHANNEL_ID || '',
|
|
deviceId: process.env.MOCK_SIM_GATEWAY_DEVICE_ID || 'child-001',
|
|
groupId: process.env.MOCK_SIM_GATEWAY_GROUP_ID || '',
|
|
sourceId: process.env.MOCK_SIM_GATEWAY_SOURCE_ID || 'mock-gps-sim',
|
|
sourceMode: process.env.MOCK_SIM_GATEWAY_SOURCE_MODE || 'mock',
|
|
reconnectMs: Math.max(1000, Number(process.env.MOCK_SIM_GATEWAY_RECONNECT_MS) || 3000),
|
|
}
|
|
|
|
function getContentType(filePath) {
|
|
const ext = path.extname(filePath).toLowerCase()
|
|
if (ext === '.html') {
|
|
return 'text/html; charset=utf-8'
|
|
}
|
|
if (ext === '.css') {
|
|
return 'text/css; charset=utf-8'
|
|
}
|
|
if (ext === '.js') {
|
|
return 'application/javascript; charset=utf-8'
|
|
}
|
|
if (ext === '.json') {
|
|
return 'application/json; charset=utf-8'
|
|
}
|
|
if (ext === '.svg') {
|
|
return 'image/svg+xml'
|
|
}
|
|
return 'text/plain; charset=utf-8'
|
|
}
|
|
|
|
function respondJson(response, statusCode, payload) {
|
|
response.writeHead(statusCode, {
|
|
'Content-Type': 'application/json; charset=utf-8',
|
|
'Cache-Control': 'no-store',
|
|
'Access-Control-Allow-Origin': '*',
|
|
})
|
|
response.end(JSON.stringify(payload))
|
|
}
|
|
|
|
function serveStatic(requestPath, response) {
|
|
let safePath = requestPath === '/' ? '/index.html' : requestPath
|
|
if (safePath.endsWith('/')) {
|
|
safePath = `${safePath}index.html`
|
|
} else if (!path.extname(safePath)) {
|
|
safePath = `${safePath}/index.html`
|
|
}
|
|
const resolvedPath = path.normalize(path.join(PUBLIC_DIR, safePath))
|
|
if (!resolvedPath.startsWith(PUBLIC_DIR)) {
|
|
response.writeHead(403)
|
|
response.end('Forbidden')
|
|
return
|
|
}
|
|
|
|
fs.readFile(resolvedPath, (error, content) => {
|
|
if (error) {
|
|
response.writeHead(404)
|
|
response.end('Not Found')
|
|
return
|
|
}
|
|
|
|
response.writeHead(200, {
|
|
'Content-Type': getContentType(resolvedPath),
|
|
'Cache-Control': 'no-store',
|
|
})
|
|
response.end(content)
|
|
})
|
|
}
|
|
|
|
function isMockGpsPayload(payload) {
|
|
return payload
|
|
&& payload.type === 'mock_gps'
|
|
&& Number.isFinite(payload.lat)
|
|
&& Number.isFinite(payload.lon)
|
|
}
|
|
|
|
function isMockHeartRatePayload(payload) {
|
|
return payload
|
|
&& payload.type === 'mock_heart_rate'
|
|
&& Number.isFinite(payload.bpm)
|
|
}
|
|
|
|
function isDebugLogPayload(payload) {
|
|
return payload
|
|
&& payload.type === 'debug-log'
|
|
&& typeof payload.scope === 'string'
|
|
&& typeof payload.level === 'string'
|
|
&& typeof payload.message === 'string'
|
|
}
|
|
|
|
async function handleProxyRequest(request, response) {
|
|
const requestUrl = new URL(request.url || '/', `http://127.0.0.1:${PORT}`)
|
|
const targetUrl = requestUrl.searchParams.get('url')
|
|
if (!targetUrl) {
|
|
response.writeHead(400, {
|
|
'Content-Type': 'text/plain; charset=utf-8',
|
|
'Access-Control-Allow-Origin': '*',
|
|
})
|
|
response.end('Missing url')
|
|
return
|
|
}
|
|
|
|
try {
|
|
const upstream = await fetch(targetUrl)
|
|
const body = Buffer.from(await upstream.arrayBuffer())
|
|
response.writeHead(upstream.status, {
|
|
'Content-Type': upstream.headers.get('content-type') || 'application/octet-stream',
|
|
'Cache-Control': 'no-store',
|
|
'Access-Control-Allow-Origin': '*',
|
|
})
|
|
response.end(body)
|
|
} catch (error) {
|
|
response.writeHead(502, {
|
|
'Content-Type': 'text/plain; charset=utf-8',
|
|
'Access-Control-Allow-Origin': '*',
|
|
})
|
|
response.end(error && error.message ? error.message : 'Proxy request failed')
|
|
}
|
|
}
|
|
|
|
async function readJsonBody(request) {
|
|
return new Promise((resolve, reject) => {
|
|
const chunks = []
|
|
request.on('data', (chunk) => {
|
|
chunks.push(chunk)
|
|
})
|
|
request.on('end', () => {
|
|
const raw = Buffer.concat(chunks).toString('utf8').trim()
|
|
if (!raw) {
|
|
resolve({})
|
|
return
|
|
}
|
|
try {
|
|
resolve(JSON.parse(raw))
|
|
} catch (error) {
|
|
reject(error)
|
|
}
|
|
})
|
|
request.on('error', reject)
|
|
})
|
|
}
|
|
|
|
function normalizeBridgeConfig(input, currentConfig) {
|
|
const source = input || {}
|
|
const fallback = currentConfig || INITIAL_BRIDGE_CONFIG
|
|
|
|
return {
|
|
enabled: typeof source.enabled === 'boolean' ? source.enabled : fallback.enabled,
|
|
url: typeof source.url === 'string' && source.url.trim() ? source.url.trim() : fallback.url,
|
|
token: typeof source.token === 'string' ? source.token.trim() : fallback.token,
|
|
channelId: typeof source.channelId === 'string' ? source.channelId.trim() : fallback.channelId,
|
|
deviceId: typeof source.deviceId === 'string' && source.deviceId.trim() ? source.deviceId.trim() : fallback.deviceId,
|
|
groupId: typeof source.groupId === 'string' ? source.groupId.trim() : fallback.groupId,
|
|
sourceId: typeof source.sourceId === 'string' && source.sourceId.trim() ? source.sourceId.trim() : fallback.sourceId,
|
|
sourceMode: typeof source.sourceMode === 'string' && source.sourceMode.trim() ? source.sourceMode.trim() : fallback.sourceMode,
|
|
reconnectMs: Math.max(1000, Number(source.reconnectMs) || fallback.reconnectMs),
|
|
}
|
|
}
|
|
|
|
function createGatewayBridge() {
|
|
const bridgeState = {
|
|
config: { ...INITIAL_BRIDGE_CONFIG },
|
|
socket: null,
|
|
connecting: false,
|
|
connected: false,
|
|
authenticated: false,
|
|
reconnectTimer: 0,
|
|
lastError: '',
|
|
lastSentAt: 0,
|
|
lastSentTopic: '',
|
|
sentCount: 0,
|
|
droppedCount: 0,
|
|
}
|
|
|
|
function logBridge(message) {
|
|
console.log(`[gateway-bridge] ${message}`)
|
|
}
|
|
|
|
function clearReconnectTimer() {
|
|
if (!bridgeState.reconnectTimer) {
|
|
return
|
|
}
|
|
clearTimeout(bridgeState.reconnectTimer)
|
|
bridgeState.reconnectTimer = 0
|
|
}
|
|
|
|
function scheduleReconnect() {
|
|
if (!bridgeState.config.enabled || bridgeState.reconnectTimer) {
|
|
return
|
|
}
|
|
bridgeState.reconnectTimer = setTimeout(() => {
|
|
bridgeState.reconnectTimer = 0
|
|
connect()
|
|
}, bridgeState.config.reconnectMs)
|
|
}
|
|
|
|
function resetSocketState() {
|
|
bridgeState.socket = null
|
|
bridgeState.connecting = false
|
|
bridgeState.connected = false
|
|
bridgeState.authenticated = false
|
|
}
|
|
|
|
function handleGatewayMessage(rawMessage) {
|
|
let parsed
|
|
try {
|
|
parsed = JSON.parse(String(rawMessage))
|
|
} catch (_error) {
|
|
return
|
|
}
|
|
|
|
if (parsed.type === 'welcome') {
|
|
if (!bridgeState.socket || bridgeState.socket.readyState !== WebSocket.OPEN) {
|
|
return
|
|
}
|
|
if (bridgeState.config.channelId) {
|
|
bridgeState.socket.send(JSON.stringify({
|
|
type: 'join_channel',
|
|
role: 'producer',
|
|
channelId: bridgeState.config.channelId,
|
|
token: bridgeState.config.token,
|
|
}))
|
|
} else {
|
|
bridgeState.socket.send(JSON.stringify({
|
|
type: 'authenticate',
|
|
role: 'producer',
|
|
token: bridgeState.config.token,
|
|
}))
|
|
}
|
|
return
|
|
}
|
|
|
|
if (parsed.type === 'authenticated' || parsed.type === 'joined_channel') {
|
|
bridgeState.authenticated = true
|
|
bridgeState.lastError = ''
|
|
if (bridgeState.config.channelId) {
|
|
logBridge(`joined channel=${bridgeState.config.channelId}, device=${bridgeState.config.deviceId}, source=${bridgeState.config.sourceId}`)
|
|
} else {
|
|
logBridge(`authenticated, device=${bridgeState.config.deviceId}, source=${bridgeState.config.sourceId}`)
|
|
}
|
|
return
|
|
}
|
|
|
|
if (parsed.type === 'error') {
|
|
bridgeState.lastError = parsed.error || 'gateway error'
|
|
logBridge(`error: ${bridgeState.lastError}`)
|
|
}
|
|
}
|
|
|
|
function closeSocket() {
|
|
if (!bridgeState.socket) {
|
|
return
|
|
}
|
|
try {
|
|
bridgeState.socket.close()
|
|
} catch (_error) {
|
|
// noop
|
|
}
|
|
resetSocketState()
|
|
}
|
|
|
|
function connect() {
|
|
if (!bridgeState.config.enabled || bridgeState.connecting) {
|
|
return
|
|
}
|
|
if (bridgeState.socket && (bridgeState.socket.readyState === WebSocket.OPEN || bridgeState.socket.readyState === WebSocket.CONNECTING)) {
|
|
return
|
|
}
|
|
|
|
clearReconnectTimer()
|
|
bridgeState.connecting = true
|
|
bridgeState.lastError = ''
|
|
logBridge(`connecting to ${bridgeState.config.url}`)
|
|
|
|
const socket = new WebSocket(bridgeState.config.url)
|
|
bridgeState.socket = socket
|
|
|
|
socket.on('open', () => {
|
|
bridgeState.connecting = false
|
|
bridgeState.connected = true
|
|
logBridge('connected')
|
|
})
|
|
|
|
socket.on('message', handleGatewayMessage)
|
|
|
|
socket.on('close', () => {
|
|
const wasConnected = bridgeState.connected || bridgeState.authenticated
|
|
resetSocketState()
|
|
if (wasConnected) {
|
|
logBridge('disconnected')
|
|
}
|
|
scheduleReconnect()
|
|
})
|
|
|
|
socket.on('error', (error) => {
|
|
bridgeState.lastError = error && error.message ? error.message : 'gateway socket error'
|
|
logBridge(`socket error: ${bridgeState.lastError}`)
|
|
})
|
|
}
|
|
|
|
function toGatewayEnvelope(payload) {
|
|
if (isMockGpsPayload(payload)) {
|
|
return {
|
|
schemaVersion: 1,
|
|
messageId: `gps-${payload.timestamp}`,
|
|
timestamp: payload.timestamp,
|
|
topic: 'telemetry.location',
|
|
source: {
|
|
kind: 'producer',
|
|
id: bridgeState.config.sourceId,
|
|
mode: bridgeState.config.sourceMode,
|
|
},
|
|
target: {
|
|
channelId: bridgeState.config.channelId,
|
|
deviceId: bridgeState.config.deviceId,
|
|
groupId: bridgeState.config.groupId,
|
|
},
|
|
payload: {
|
|
lat: Number(payload.lat),
|
|
lng: Number(payload.lon),
|
|
speed: Number(payload.speedMps) || 0,
|
|
bearing: Number(payload.headingDeg) || 0,
|
|
accuracy: Number(payload.accuracyMeters) || 6,
|
|
coordSystem: 'GCJ02',
|
|
},
|
|
}
|
|
}
|
|
|
|
if (isMockHeartRatePayload(payload)) {
|
|
return {
|
|
schemaVersion: 1,
|
|
messageId: `hr-${payload.timestamp}`,
|
|
timestamp: payload.timestamp,
|
|
topic: 'telemetry.heart_rate',
|
|
source: {
|
|
kind: 'producer',
|
|
id: bridgeState.config.sourceId,
|
|
mode: bridgeState.config.sourceMode,
|
|
},
|
|
target: {
|
|
channelId: bridgeState.config.channelId,
|
|
deviceId: bridgeState.config.deviceId,
|
|
groupId: bridgeState.config.groupId,
|
|
},
|
|
payload: {
|
|
bpm: Math.max(1, Math.round(Number(payload.bpm))),
|
|
},
|
|
}
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
function publish(payload) {
|
|
if (!bridgeState.config.enabled) {
|
|
return
|
|
}
|
|
|
|
if (!bridgeState.socket || bridgeState.socket.readyState !== WebSocket.OPEN || !bridgeState.authenticated) {
|
|
bridgeState.droppedCount += 1
|
|
connect()
|
|
return
|
|
}
|
|
|
|
const envelope = toGatewayEnvelope(payload)
|
|
if (!envelope) {
|
|
return
|
|
}
|
|
|
|
bridgeState.socket.send(JSON.stringify({
|
|
type: 'publish',
|
|
envelope,
|
|
}))
|
|
bridgeState.lastSentAt = Date.now()
|
|
bridgeState.lastSentTopic = envelope.topic
|
|
bridgeState.sentCount += 1
|
|
}
|
|
|
|
function updateConfig(nextConfigInput) {
|
|
const nextConfig = normalizeBridgeConfig(nextConfigInput, bridgeState.config)
|
|
const changed = JSON.stringify(nextConfig) !== JSON.stringify(bridgeState.config)
|
|
bridgeState.config = nextConfig
|
|
|
|
if (!changed) {
|
|
return getStatus()
|
|
}
|
|
|
|
bridgeState.lastError = ''
|
|
if (!bridgeState.config.enabled) {
|
|
clearReconnectTimer()
|
|
closeSocket()
|
|
logBridge('disabled')
|
|
return getStatus()
|
|
}
|
|
|
|
clearReconnectTimer()
|
|
closeSocket()
|
|
connect()
|
|
return getStatus()
|
|
}
|
|
|
|
function getConfig() {
|
|
return { ...bridgeState.config }
|
|
}
|
|
|
|
function getStatus() {
|
|
return {
|
|
enabled: bridgeState.config.enabled,
|
|
url: bridgeState.config.url,
|
|
connected: bridgeState.connected,
|
|
authenticated: bridgeState.authenticated,
|
|
channelId: bridgeState.config.channelId,
|
|
deviceId: bridgeState.config.deviceId,
|
|
groupId: bridgeState.config.groupId,
|
|
sourceId: bridgeState.config.sourceId,
|
|
sourceMode: bridgeState.config.sourceMode,
|
|
reconnectMs: bridgeState.config.reconnectMs,
|
|
hasToken: Boolean(bridgeState.config.token),
|
|
sentCount: bridgeState.sentCount,
|
|
droppedCount: bridgeState.droppedCount,
|
|
lastSentAt: bridgeState.lastSentAt,
|
|
lastSentTopic: bridgeState.lastSentTopic,
|
|
lastError: bridgeState.lastError,
|
|
}
|
|
}
|
|
|
|
if (bridgeState.config.enabled) {
|
|
connect()
|
|
}
|
|
|
|
return {
|
|
publish,
|
|
updateConfig,
|
|
getConfig,
|
|
getStatus,
|
|
}
|
|
}
|
|
|
|
const gatewayBridge = createGatewayBridge()
|
|
|
|
const server = http.createServer((request, response) => {
|
|
if (request.method === 'OPTIONS') {
|
|
response.writeHead(204, {
|
|
'Access-Control-Allow-Origin': '*',
|
|
'Access-Control-Allow-Methods': 'GET,POST,OPTIONS',
|
|
'Access-Control-Allow-Headers': 'Content-Type',
|
|
})
|
|
response.end()
|
|
return
|
|
}
|
|
|
|
if ((request.url || '').startsWith(PROXY_PATH)) {
|
|
handleProxyRequest(request, response)
|
|
return
|
|
}
|
|
|
|
if ((request.url || '').startsWith(BRIDGE_CONFIG_PATH)) {
|
|
if (request.method === 'GET') {
|
|
respondJson(response, 200, {
|
|
config: gatewayBridge.getConfig(),
|
|
status: gatewayBridge.getStatus(),
|
|
})
|
|
return
|
|
}
|
|
|
|
if (request.method === 'POST') {
|
|
readJsonBody(request)
|
|
.then((payload) => {
|
|
const status = gatewayBridge.updateConfig(payload)
|
|
respondJson(response, 200, {
|
|
config: gatewayBridge.getConfig(),
|
|
status,
|
|
})
|
|
})
|
|
.catch((error) => {
|
|
respondJson(response, 400, {
|
|
error: error && error.message ? error.message : 'Invalid JSON body',
|
|
})
|
|
})
|
|
return
|
|
}
|
|
|
|
respondJson(response, 405, {
|
|
error: 'Method Not Allowed',
|
|
})
|
|
return
|
|
}
|
|
|
|
if ((request.url || '').startsWith(BRIDGE_STATUS_PATH)) {
|
|
respondJson(response, 200, gatewayBridge.getStatus())
|
|
return
|
|
}
|
|
|
|
serveStatic(request.url || '/', response)
|
|
})
|
|
|
|
const gpsWss = new WebSocketServer({ noServer: true })
|
|
const heartRateWss = new WebSocketServer({ noServer: true })
|
|
const debugLogWss = new WebSocketServer({ noServer: true })
|
|
|
|
gpsWss.on('connection', (socket) => {
|
|
socket.on('message', (rawMessage) => {
|
|
const text = String(rawMessage)
|
|
let parsed
|
|
try {
|
|
parsed = JSON.parse(text)
|
|
} catch (_error) {
|
|
return
|
|
}
|
|
|
|
if (!isMockGpsPayload(parsed)) {
|
|
return
|
|
}
|
|
|
|
const outgoing = JSON.stringify({
|
|
type: 'mock_gps',
|
|
timestamp: Number.isFinite(parsed.timestamp) ? parsed.timestamp : Date.now(),
|
|
lat: Number(parsed.lat),
|
|
lon: Number(parsed.lon),
|
|
accuracyMeters: Number.isFinite(parsed.accuracyMeters) ? Number(parsed.accuracyMeters) : 6,
|
|
speedMps: Number.isFinite(parsed.speedMps) ? Number(parsed.speedMps) : 0,
|
|
headingDeg: Number.isFinite(parsed.headingDeg) ? Number(parsed.headingDeg) : 0,
|
|
})
|
|
gatewayBridge.publish(JSON.parse(outgoing))
|
|
|
|
gpsWss.clients.forEach((client) => {
|
|
if (client.readyState === client.OPEN) {
|
|
client.send(outgoing)
|
|
}
|
|
})
|
|
})
|
|
})
|
|
|
|
heartRateWss.on('connection', (socket) => {
|
|
socket.on('message', (rawMessage) => {
|
|
const text = String(rawMessage)
|
|
let parsed
|
|
try {
|
|
parsed = JSON.parse(text)
|
|
} catch (_error) {
|
|
return
|
|
}
|
|
|
|
if (!isMockHeartRatePayload(parsed)) {
|
|
return
|
|
}
|
|
|
|
const outgoing = JSON.stringify({
|
|
type: 'mock_heart_rate',
|
|
timestamp: Number.isFinite(parsed.timestamp) ? parsed.timestamp : Date.now(),
|
|
bpm: Math.max(1, Math.round(Number(parsed.bpm))),
|
|
})
|
|
gatewayBridge.publish(JSON.parse(outgoing))
|
|
|
|
heartRateWss.clients.forEach((client) => {
|
|
if (client.readyState === client.OPEN) {
|
|
client.send(outgoing)
|
|
}
|
|
})
|
|
})
|
|
})
|
|
|
|
debugLogWss.on('connection', (socket) => {
|
|
socket.on('message', (rawMessage) => {
|
|
const text = String(rawMessage)
|
|
let parsed
|
|
try {
|
|
parsed = JSON.parse(text)
|
|
} catch (_error) {
|
|
return
|
|
}
|
|
|
|
if (!isDebugLogPayload(parsed)) {
|
|
return
|
|
}
|
|
|
|
const outgoing = JSON.stringify({
|
|
type: 'debug-log',
|
|
timestamp: Number.isFinite(parsed.timestamp) ? parsed.timestamp : Date.now(),
|
|
scope: String(parsed.scope || 'app').slice(0, 64),
|
|
level: parsed.level === 'warn' || parsed.level === 'error' ? parsed.level : 'info',
|
|
message: String(parsed.message || '').slice(0, 400),
|
|
...(parsed.payload && typeof parsed.payload === 'object'
|
|
? { payload: parsed.payload }
|
|
: {}),
|
|
})
|
|
|
|
debugLogWss.clients.forEach((client) => {
|
|
if (client.readyState === client.OPEN) {
|
|
client.send(outgoing)
|
|
}
|
|
})
|
|
})
|
|
})
|
|
|
|
server.on('upgrade', (request, socket, head) => {
|
|
const requestUrl = request.url || ''
|
|
if (requestUrl.startsWith(GPS_WS_PATH)) {
|
|
gpsWss.handleUpgrade(request, socket, head, (ws) => {
|
|
gpsWss.emit('connection', ws, request)
|
|
})
|
|
return
|
|
}
|
|
|
|
if (requestUrl.startsWith(HEART_RATE_WS_PATH)) {
|
|
heartRateWss.handleUpgrade(request, socket, head, (ws) => {
|
|
heartRateWss.emit('connection', ws, request)
|
|
})
|
|
return
|
|
}
|
|
|
|
if (requestUrl.startsWith(DEBUG_LOG_WS_PATH)) {
|
|
debugLogWss.handleUpgrade(request, socket, head, (ws) => {
|
|
debugLogWss.emit('connection', ws, request)
|
|
})
|
|
return
|
|
}
|
|
|
|
if (!requestUrl) {
|
|
socket.destroy()
|
|
}
|
|
socket.destroy()
|
|
})
|
|
|
|
server.listen(PORT, HOST, () => {
|
|
console.log(`Mock GPS simulator running:`)
|
|
console.log(` UI: http://127.0.0.1:${PORT}/`)
|
|
console.log(` GPS WS: ws://127.0.0.1:${PORT}${GPS_WS_PATH}`)
|
|
console.log(` HR WS: ws://127.0.0.1:${PORT}${HEART_RATE_WS_PATH}`)
|
|
console.log(` Logger WS: ws://127.0.0.1:${PORT}${DEBUG_LOG_WS_PATH}`)
|
|
console.log(` Proxy: http://127.0.0.1:${PORT}${PROXY_PATH}?url=<remote-url>`)
|
|
console.log(` Bridge status: http://127.0.0.1:${PORT}${BRIDGE_STATUS_PATH}`)
|
|
console.log(` Bridge config: http://127.0.0.1:${PORT}${BRIDGE_CONFIG_PATH}`)
|
|
if (INITIAL_BRIDGE_CONFIG.enabled) {
|
|
console.log(` Gateway bridge: enabled -> ${INITIAL_BRIDGE_CONFIG.url}`)
|
|
console.log(` Gateway target device: ${INITIAL_BRIDGE_CONFIG.deviceId}`)
|
|
} else {
|
|
console.log(` Gateway bridge: disabled`)
|
|
}
|
|
})
|