Full-screen /kiosk route, separate from the staff SPA: dedicated long-lived kiosk login (own password, hashed, mints its own hnf_session cookie locally via CENTRAL_AUTH_SECRET -- no central auth changes needed), scrollable grid of small mode/setpoint blocks per live-controllable device (shelly_trv, mhi_mqtt, daikin), tap-to-edit modal reusing the existing per-type control panels, and a type-aware bulk multiselect (separate TRV/AC sections, only applied to matching selected devices, TRV commands staggered). - backend/src/routes/kiosk.js: public POST /api/kiosk/login - backend/src/routes/settings.js: admin set/check kiosk password (bcrypt) - backend/src/routes/config.js: exclude/protect kiosk_password_hash - frontend/src/pages/Kiosk.tsx + components/Kiosk*: the kiosk UI - reuses GET /api/status for the combined device list -- no new status endpoint needed since the kiosk token now also carries the 'view' cap Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
45 lines
2 KiB
JavaScript
45 lines
2 KiB
JavaScript
import bcrypt from 'bcryptjs'
|
|
import { requireAuth, requireCap } from '../auth.js'
|
|
import { testConnection } from '../lib/newbook.js'
|
|
import { isConnected as mqttConnected } from '../lib/mqtt.js'
|
|
import { getConfig, setConfig } from '../db.js'
|
|
|
|
// Settings-page-backing endpoints that don't fit the generic config.js key/value
|
|
// store: connection tests and the MQTT broker's live status. The Home Assistant
|
|
// integration itself is configured centrally in the shared `settings` app, not
|
|
// here — this just reports whether it's reachable (via the driver's own no-op
|
|
// behaviour when unconfigured).
|
|
export async function settingsRoutes(app) {
|
|
app.addHook('preHandler', requireAuth)
|
|
|
|
app.post('/api/settings/newbook-test', { preHandler: requireCap('settings') }, async () => {
|
|
return testConnection()
|
|
})
|
|
|
|
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 not be provisioned yet, or hvac-backend has no credentials in settings.',
|
|
}
|
|
})
|
|
|
|
// Kiosk wall-panel login (see routes/kiosk.js) — the password itself is never
|
|
// readable back, only whether one has been set, so the Settings page can show
|
|
// a "configured" state without exposing the hash to the browser.
|
|
app.get('/api/settings/kiosk-password-status', { preHandler: requireCap('settings') }, async () => {
|
|
const hash = await getConfig('kiosk_password_hash', null)
|
|
return { configured: !!hash }
|
|
})
|
|
|
|
app.post('/api/settings/kiosk-password', { preHandler: requireCap('settings') }, async (req, reply) => {
|
|
const { password } = req.body || {}
|
|
if (!password || typeof password !== 'string' || password.length < 6) {
|
|
return reply.status(400).send({ error: 'Password must be at least 6 characters' })
|
|
}
|
|
const hash = await bcrypt.hash(password, 12)
|
|
await setConfig('kiosk_password_hash', hash)
|
|
return { ok: true }
|
|
})
|
|
}
|