Scaffold plant app - MQTT monitoring for boiler-room equipment
Read-only monitoring/alerting for boilers, water softener, calorifiers and pumps via a generic MQTT-topic-prefix asset model, so new equipment can be onboarded without new ingestion code. Threshold and stale-data alert rules with email + in-app notification. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
commit
503b397dff
38 changed files with 5044 additions and 0 deletions
42
backend/src/routes/settings.js
Normal file
42
backend/src/routes/settings.js
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import { requireAuth, requireCap } from '../auth.js'
|
||||
import { pool } from '../db.js'
|
||||
import { isConnected as mqttConnected } from '../lib/mqtt.js'
|
||||
|
||||
// Standard app_settings pattern (see conventions doc's "Cross-app integration
|
||||
// credentials" section) — plant only has one setting today (who gets alert
|
||||
// emails), but the shape is kept generic so a future integration is a one-row
|
||||
// addition to ALLOWED_KEYS + the seed default in db.js, not a new pattern.
|
||||
const ALLOWED_KEYS = new Set(['alert_notify_email'])
|
||||
|
||||
export async function settingsRoutes(app) {
|
||||
app.addHook('preHandler', requireAuth)
|
||||
|
||||
app.get('/api/settings', { preHandler: requireCap('settings') }, async () => {
|
||||
const { rows } = await pool.query('SELECT key, value, updated_at FROM app_settings ORDER BY key')
|
||||
return { settings: rows }
|
||||
})
|
||||
|
||||
app.put('/api/settings', { preHandler: requireCap('settings') }, async (req, reply) => {
|
||||
const { settings } = req.body || {}
|
||||
if (!Array.isArray(settings)) return reply.status(400).send({ error: 'settings must be an array' })
|
||||
|
||||
for (const { key, value } of settings) {
|
||||
if (!ALLOWED_KEYS.has(key)) continue
|
||||
await pool.query(
|
||||
`INSERT INTO app_settings (key, value, updated_at) VALUES ($1, $2, NOW())
|
||||
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, updated_at = NOW()`,
|
||||
[key, value ?? '']
|
||||
)
|
||||
}
|
||||
return { ok: true }
|
||||
})
|
||||
|
||||
app.get('/api/settings/mqtt-status', { preHandler: requireCap('settings') }, async () => {
|
||||
return {
|
||||
connected: mqttConnected(),
|
||||
note: mqttConnected()
|
||||
? 'Connected to the shared MQTT broker.'
|
||||
: 'Not connected — the shared MQTT broker (LXC 104) may be unreachable, or plant-backend has no credentials in settings.',
|
||||
}
|
||||
})
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue