portal: 'service consumer' checkbox for MQTT broker clients
Surfaces settings' new service-client flag in the MQTT Broker Clients tab — a checkbox that stores the password encrypted so a stack app (e.g. hvac-backend) can fetch its own login at runtime, plus a 'service' badge in the client list. Device logins are unchanged (leave the box off). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
d1b253a00c
commit
411911c4c8
1 changed files with 24 additions and 2 deletions
|
|
@ -929,6 +929,7 @@ interface MqttClient {
|
|||
topic_scope: string
|
||||
can_publish: boolean
|
||||
can_subscribe: boolean
|
||||
is_service: boolean
|
||||
active: boolean
|
||||
created_at: string
|
||||
revoked_at: string | null
|
||||
|
|
@ -942,6 +943,7 @@ function MqttClientsTab() {
|
|||
const [topicScope, setTopicScope] = useState('')
|
||||
const [canPublish, setCanPublish] = useState(false)
|
||||
const [canSubscribe, setCanSubscribe] = useState(true)
|
||||
const [isService, setIsService] = useState(false)
|
||||
const [creating, setCreating] = useState(false)
|
||||
const [error, setError] = useState('')
|
||||
const [created, setCreated] = useState<{ username: string; password: string } | null>(null)
|
||||
|
|
@ -969,14 +971,14 @@ function MqttClientsTab() {
|
|||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
name: name.trim(), topic_scope: topicScope.trim(),
|
||||
can_publish: canPublish, can_subscribe: canSubscribe,
|
||||
can_publish: canPublish, can_subscribe: canSubscribe, is_service: isService,
|
||||
}),
|
||||
})
|
||||
const data = await res.json()
|
||||
if (!res.ok) { setError(data.error || 'Failed to create client'); return }
|
||||
setCreated({ username: data.username, password: data.password })
|
||||
setShowForm(false)
|
||||
setName(''); setTopicScope(''); setCanPublish(false); setCanSubscribe(true)
|
||||
setName(''); setTopicScope(''); setCanPublish(false); setCanSubscribe(true); setIsService(false)
|
||||
load()
|
||||
} finally {
|
||||
setCreating(false)
|
||||
|
|
@ -1050,6 +1052,17 @@ function MqttClientsTab() {
|
|||
<input type="checkbox" checked={canSubscribe} onChange={e => setCanSubscribe(e.target.checked)} /> Can subscribe
|
||||
</label>
|
||||
</div>
|
||||
<label style={{ display: 'flex', alignItems: 'flex-start', gap: '0.4rem', fontSize: '0.85rem', color: 'var(--text-dark)' }}>
|
||||
<input type="checkbox" checked={isService} onChange={e => setIsService(e.target.checked)} style={{ marginTop: '0.2rem' }} />
|
||||
<span>
|
||||
Service consumer (stack app)
|
||||
<span style={{ display: 'block', fontSize: '0.76rem', color: 'var(--text-mid)', fontWeight: 400 }}>
|
||||
Stores the password encrypted so a stack app can fetch its own login at runtime — needed for apps like
|
||||
<code style={{ margin: '0 0.25em' }}>hvac-backend</code> that reconnect on restart. Name it to match the app's
|
||||
client id; its username becomes the name's slug (no random suffix). Leave off for device logins (Shelly, gateways).
|
||||
</span>
|
||||
</span>
|
||||
</label>
|
||||
<div>
|
||||
<button type="submit" disabled={creating} style={{
|
||||
background: 'var(--gold)', color: 'var(--navy)', border: 'none',
|
||||
|
|
@ -1119,6 +1132,15 @@ function MqttClientsTab() {
|
|||
<span style={{ marginLeft: '0.5rem', fontFamily: 'monospace', fontSize: '0.78rem', color: 'var(--text-mid)', fontWeight: 400 }}>
|
||||
{c.username}
|
||||
</span>
|
||||
{c.is_service && (
|
||||
<span style={{
|
||||
marginLeft: '0.5rem', fontSize: '0.68rem', fontWeight: 600, color: 'var(--navy)',
|
||||
background: 'var(--surface-2)', border: '1px solid var(--card-border)',
|
||||
borderRadius: '4px', padding: '0.05rem 0.4rem', verticalAlign: 'middle',
|
||||
}}>
|
||||
service
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div style={{ fontSize: '0.72rem', color: 'var(--text-mid)', fontFamily: 'monospace' }}>
|
||||
{c.topic_scope}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue