Add kiosk wall-panel control view
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>
This commit is contained in:
parent
f0b4ac3c41
commit
2db7d5f4d0
16 changed files with 757 additions and 14 deletions
|
|
@ -1,6 +1,8 @@
|
|||
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
|
||||
|
|
@ -22,4 +24,22 @@ export async function settingsRoutes(app) {
|
|||
: '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 }
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue