hvac: fetch MQTT credentials from Settings (drop .env secret)

getCredentials() now fetches the hvac-backend broker login from Settings'
new internal service-client endpoint (GET /internal/mqtt-client/hvac-backend,
bearer SETTINGS_SECRET) — same runtime-fetch pattern as newbook.js, no
broker secret in this app's .env. Env MQTT_* vars remain a dev-only override.
Drops the MQTT_USERNAME/PASSWORD compose passthrough added earlier.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
jtricerolph 2026-08-13 13:42:58 +00:00
parent c873e4a270
commit 6325532cfe
2 changed files with 11 additions and 14 deletions

View file

@ -82,12 +82,15 @@ let reconnectAttempt = 0
const discovered = new Map() const discovered = new Map()
async function getCredentials() { async function getCredentials() {
// Primary path: env-injected credentials for this app's own dynsec client. // Fetch this app's broker login from the central Settings service at runtime —
// The installer provisions a `hvac-backend` broker client and writes its // same pattern (and same SETTINGS_SECRET) as newbook.js's getCredentials().
// creds into /opt/hvac/.env. This is the working model because the broker // An admin creates a `hvac-backend` *service* MQTT client on the Settings
// stores each client's password show-once (Mosquitto dynsec hashes it), so // "MQTT Broker Clients" page; Settings stores its password encrypted and serves
// it genuinely cannot be re-served from settings at runtime — settings only // it here. Nothing broker-related lives in this app's .env.
// reveals a new client's password once, at creation. //
// Env override (dev/emergency only, not set in production compose): if explicit
// MQTT_* vars are present they win, so the app can run against a local broker
// without Settings.
if (process.env.MQTT_BROKER_HOST && process.env.MQTT_USERNAME && process.env.MQTT_PASSWORD) { if (process.env.MQTT_BROKER_HOST && process.env.MQTT_USERNAME && process.env.MQTT_PASSWORD) {
return { return {
host: process.env.MQTT_BROKER_HOST, host: process.env.MQTT_BROKER_HOST,
@ -96,14 +99,12 @@ async function getCredentials() {
password: process.env.MQTT_PASSWORD, password: process.env.MQTT_PASSWORD,
} }
} }
// Fallback: settings service (kept for the eventual self-service model — not const url = `${process.env.SETTINGS_URL}/settings/api/internal/mqtt-client/${CLIENT_NAME}`
// wired yet; settings has no internal MQTT-credential serve endpoint today).
const url = `${process.env.SETTINGS_URL}/settings/api/internal/integration/mqtt?client=${CLIENT_NAME}`
const res = await fetch(url, { const res = await fetch(url, {
headers: { Authorization: `Bearer ${process.env.SETTINGS_SECRET}` }, headers: { Authorization: `Bearer ${process.env.SETTINGS_SECRET}` },
signal: AbortSignal.timeout(5000), signal: AbortSignal.timeout(5000),
}) })
if (!res.ok) throw new Error(`Settings service returned ${res.status} fetching MQTT credentials`) if (!res.ok) throw new Error(`Settings service returned ${res.status} fetching MQTT credentials (create a "${CLIENT_NAME}" service client in Settings)`)
const s = await res.json() const s = await res.json()
if (!s.host || !s.username || !s.password) throw new Error('MQTT broker credentials not configured in settings') if (!s.host || !s.username || !s.password) throw new Error('MQTT broker credentials not configured in settings')
return { host: s.host, port: s.port || 1883, username: s.username, password: s.password } return { host: s.host, port: s.port || 1883, username: s.username, password: s.password }

View file

@ -8,10 +8,6 @@ services:
- CENTRAL_AUTH_SECRET=${CENTRAL_AUTH_SECRET} - CENTRAL_AUTH_SECRET=${CENTRAL_AUTH_SECRET}
- SETTINGS_URL=${SETTINGS_URL} - SETTINGS_URL=${SETTINGS_URL}
- SETTINGS_SECRET=${SETTINGS_SECRET} - SETTINGS_SECRET=${SETTINGS_SECRET}
- MQTT_BROKER_HOST=${MQTT_BROKER_HOST:-10.10.10.104}
- MQTT_BROKER_PORT=${MQTT_BROKER_PORT:-1883}
- MQTT_USERNAME=${MQTT_USERNAME:-}
- MQTT_PASSWORD=${MQTT_PASSWORD:-}
- APP_SLUG=hvac - APP_SLUG=hvac
- OFFICE_IP_CHECK=${OFFICE_IP_CHECK:-disabled} - OFFICE_IP_CHECK=${OFFICE_IP_CHECK:-disabled}
- NEWBOOK_LOCATION_ID=${NEWBOOK_LOCATION_ID:-} - NEWBOOK_LOCATION_ID=${NEWBOOK_LOCATION_ID:-}