feat(ui): Fleet Pair / Revoke spire UI (#9/#12)
Some checks failed
ci.yml / feat(ui): Fleet Pair / Revoke spire UI (#9/#12) (push) Failing after 0s
Some checks failed
ci.yml / feat(ui): Fleet Pair / Revoke spire UI (#9/#12) (push) Failing after 0s
Operator-facing front for POST /machines/{id}/pair + /revoke (#21/#23):
- Pairing chip per machine row (paired / not-paired + paired-at tooltip).
- 'Pair' (qr_code_2) opens a dialog -> relays + optional duration_hours
-> POST /pair -> renders the seed_url as <lnbits-qrcode> + copy, shows
the bunker-minted spire npub. Re-pair relabels.
- 'Revoke' (link_off, shown when paired) -> confirm -> POST /revoke ->
updates the row, reports revoked_count (>=1 cut / 0 never-bound).
- Row reflects the bunker-minted identity immediately (machine_npub <-
spire_pubkey_hex, paired_at).
Quasar-UMD conventions: explicit close tags, ${ } delimiters, :style.
JS syntax-checked, conforms to .prettierrc; 210 backend tests unaffected.
Needs a manual browser smoke (superuser-gated page).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
846d5d9d0f
commit
a18f653ca7
2 changed files with 208 additions and 0 deletions
|
|
@ -131,6 +131,17 @@
|
|||
<div :style="{fontWeight: 500}" v-text="props.row.name || 'Unnamed'"></div>
|
||||
<div :style="{fontSize: '0.8em', opacity: 0.6}"
|
||||
v-text="props.row.location || 'No location set'"></div>
|
||||
<q-chip v-if="props.row.paired_at"
|
||||
dense size="sm" color="green-2" text-color="green-9"
|
||||
icon="link" :style="{marginTop: '2px'}">
|
||||
paired
|
||||
<q-tooltip>Bunker key minted; paired ${ new Date(props.row.paired_at).toLocaleString() }</q-tooltip>
|
||||
</q-chip>
|
||||
<q-chip v-else
|
||||
dense size="sm" color="grey-3" text-color="grey-8"
|
||||
icon="link_off" :style="{marginTop: '2px'}">
|
||||
not paired
|
||||
</q-chip>
|
||||
</q-td>
|
||||
<q-td key="machine_npub">
|
||||
<code :style="{fontSize: '0.85em'}"
|
||||
|
|
@ -156,6 +167,17 @@
|
|||
@click="openEditMachineDialog(props.row)">
|
||||
<q-tooltip>Edit</q-tooltip>
|
||||
</q-btn>
|
||||
<q-btn flat dense round size="sm" icon="qr_code_2"
|
||||
color="teal"
|
||||
@click="openPairDialog(props.row)">
|
||||
<q-tooltip>${ props.row.paired_at ? 'Re-pair (new seed URL)' : 'Pair (seed URL)' }</q-tooltip>
|
||||
</q-btn>
|
||||
<q-btn v-if="props.row.paired_at"
|
||||
flat dense round size="sm" icon="link_off"
|
||||
color="orange-8"
|
||||
@click="confirmRevokeMachine(props.row)">
|
||||
<q-tooltip>Revoke spire access</q-tooltip>
|
||||
</q-btn>
|
||||
<q-btn flat dense round size="sm" icon="delete"
|
||||
color="red-7"
|
||||
@click="confirmDeleteMachine(props.row)">
|
||||
|
|
@ -821,6 +843,97 @@
|
|||
</q-card>
|
||||
</q-dialog>
|
||||
|
||||
<!-- =============================================================== -->
|
||||
<!-- PAIR SPIRE DIALOG — mint bunker key + one-shot seed URL (S0/#9) -->
|
||||
<!-- =============================================================== -->
|
||||
<q-dialog v-model="pairDialog.show" persistent>
|
||||
<q-card :style="{minWidth: '480px', maxWidth: '95vw'}">
|
||||
<q-card-section class="row items-center q-pb-none">
|
||||
<div class="text-h6">Pair spire</div>
|
||||
<q-space></q-space>
|
||||
<q-btn icon="close" flat round dense v-close-popup></q-btn>
|
||||
</q-card-section>
|
||||
|
||||
<!-- Step 1 — configure + generate -->
|
||||
<q-card-section v-if="!pairDialog.result">
|
||||
<p class="text-caption q-mb-md" :style="{opacity: 0.7}">
|
||||
Mints a dedicated signing key for
|
||||
<b v-text="(pairDialog.machine && pairDialog.machine.name) || 'this spire'"></b>
|
||||
inside the operator bunker and issues a one-shot seed URL. The
|
||||
spire's key never touches its disk; its cash-outs route to this
|
||||
machine's wallet. Re-pairing issues a fresh seed.
|
||||
</p>
|
||||
|
||||
<q-input
|
||||
v-model="pairDialog.relays"
|
||||
label="Relay(s) for the spire's events"
|
||||
hint="One per line. The same relay the spire publishes to (its VITE_RELAY_URL), e.g. wss://your-host/nostrrelay/<id>"
|
||||
type="textarea" autogrow
|
||||
class="q-mb-md"
|
||||
dense outlined></q-input>
|
||||
|
||||
<q-input
|
||||
v-model.number="pairDialog.durationHours"
|
||||
label="Token lifetime in hours (optional)"
|
||||
hint="Blank = non-expiring. Set e.g. 720 (30 days) to force periodic re-pairing."
|
||||
type="number" min="1"
|
||||
class="q-mb-md"
|
||||
dense outlined></q-input>
|
||||
</q-card-section>
|
||||
<q-card-actions v-if="!pairDialog.result" align="right" class="text-primary">
|
||||
<q-btn flat label="Cancel" v-close-popup></q-btn>
|
||||
<q-btn
|
||||
color="primary" label="Generate seed URL" icon="vpn_key"
|
||||
:loading="pairDialog.saving"
|
||||
@click="submitPair"></q-btn>
|
||||
</q-card-actions>
|
||||
|
||||
<!-- Step 2 — show the seed URL -->
|
||||
<q-card-section v-else>
|
||||
<q-banner dense rounded class="bg-green-1 text-grey-9 q-mb-md">
|
||||
<template v-slot:avatar>
|
||||
<q-icon name="check_circle" color="green"></q-icon>
|
||||
</template>
|
||||
Paired. Scan this on the spire at first boot, or paste the seed URL
|
||||
into <code>provision-atm</code>. Shown once — copy it now.
|
||||
</q-banner>
|
||||
|
||||
<div class="row justify-center q-mb-md">
|
||||
<lnbits-qrcode
|
||||
:value="pairDialog.result.seed_url"
|
||||
:options="{width: 280}"></lnbits-qrcode>
|
||||
</div>
|
||||
|
||||
<q-input
|
||||
v-model="pairDialog.result.seed_url"
|
||||
label="Seed URL"
|
||||
type="textarea" autogrow readonly
|
||||
class="q-mb-sm"
|
||||
dense outlined>
|
||||
<template v-slot:append>
|
||||
<q-btn flat dense round icon="content_copy"
|
||||
@click="copy(pairDialog.result.seed_url)">
|
||||
<q-tooltip>Copy seed URL</q-tooltip>
|
||||
</q-btn>
|
||||
</template>
|
||||
</q-input>
|
||||
|
||||
<div class="text-caption" :style="{opacity: 0.7}">
|
||||
Spire identity:
|
||||
<code :style="{fontSize: '0.85em'}"
|
||||
v-text="shortNpub(pairDialog.result.spire_npub)"></code>
|
||||
<q-btn flat dense round size="xs" icon="content_copy"
|
||||
@click="copy(pairDialog.result.spire_npub)">
|
||||
<q-tooltip>Copy npub</q-tooltip>
|
||||
</q-btn>
|
||||
</div>
|
||||
</q-card-section>
|
||||
<q-card-actions v-else align="right" class="text-primary">
|
||||
<q-btn flat label="Done" color="primary" v-close-popup></q-btn>
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
|
||||
<!-- =============================================================== -->
|
||||
<!-- MACHINE DETAIL DIALOG — settlements list + per-row actions -->
|
||||
<!-- =============================================================== -->
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue