feat(pairing-ui): pre-fill the pair dialog relay with the default endpoint
Some checks failed
ci.yml / feat(pairing-ui): pre-fill the pair dialog relay with the default endpoint (pull_request) Failing after 0s
Some checks failed
ci.yml / feat(pairing-ui): pre-fill the pair dialog relay with the default endpoint (pull_request) Failing after 0s
The backend now defaults an omitted relay to the nostrclient proxy endpoint, but the pair dialog still showed an empty, client-side-required field — so the operator saw "no default". Wire it through: - GET /api/v1/dca/default-relay returns default_relay_endpoint() (the derived ws(s)://<host>/nostrclient/api/v1/relay). - openPairDialog pre-fills the relay textarea with it; the operator can override or clear it (blank → same server-side default). Drops the client-side "at least one relay is required" guard. - Hint updated to explain the default. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
c3791ed6c8
commit
4e36b98534
4 changed files with 39 additions and 9 deletions
|
|
@ -839,12 +839,21 @@ window.app = Vue.createApp({
|
||||||
// -----------------------------------------------------------------
|
// -----------------------------------------------------------------
|
||||||
// Pair / revoke spire (S0 / #9, #12)
|
// Pair / revoke spire (S0 / #9, #12)
|
||||||
// -----------------------------------------------------------------
|
// -----------------------------------------------------------------
|
||||||
openPairDialog(machine) {
|
async openPairDialog(machine) {
|
||||||
this.pairDialog.machine = machine
|
this.pairDialog.machine = machine
|
||||||
this.pairDialog.relays = ''
|
this.pairDialog.relays = ''
|
||||||
this.pairDialog.durationHours = null
|
this.pairDialog.durationHours = null
|
||||||
this.pairDialog.result = null
|
this.pairDialog.result = null
|
||||||
this.pairDialog.show = true
|
this.pairDialog.show = true
|
||||||
|
// Pre-fill with this lnbits' default relay (the nostrclient proxy
|
||||||
|
// endpoint derived from lnbits_baseurl). The operator can override or
|
||||||
|
// clear it; if left blank the backend fills in the same default.
|
||||||
|
try {
|
||||||
|
const {data} = await LNbits.api.request('GET', `${API}/default-relay`)
|
||||||
|
if (data && data.relay) this.pairDialog.relays = data.relay
|
||||||
|
} catch (e) {
|
||||||
|
// Non-fatal: leave blank; the backend still defaults on submit.
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async submitPair() {
|
async submitPair() {
|
||||||
|
|
@ -852,14 +861,12 @@ window.app = Vue.createApp({
|
||||||
.split(/[\s,]+/)
|
.split(/[\s,]+/)
|
||||||
.map(s => s.trim())
|
.map(s => s.trim())
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
if (!relays.length) {
|
// relays is optional — blank falls back to the nostrclient endpoint
|
||||||
Quasar.Notify.create({
|
// server-side (the pre-filled default), so no client-side requirement.
|
||||||
type: 'negative',
|
const body = {}
|
||||||
message: 'At least one relay is required'
|
if (relays.length) {
|
||||||
})
|
body.relays = relays
|
||||||
return
|
|
||||||
}
|
}
|
||||||
const body = {relays}
|
|
||||||
if (this.pairDialog.durationHours) {
|
if (this.pairDialog.durationHours) {
|
||||||
body.duration_hours = Number(this.pairDialog.durationHours)
|
body.duration_hours = Number(this.pairDialog.durationHours)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -868,7 +868,7 @@
|
||||||
<q-input
|
<q-input
|
||||||
v-model="pairDialog.relays"
|
v-model="pairDialog.relays"
|
||||||
label="Relay(s) for the spire's events"
|
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>"
|
hint="Pre-filled with this instance's nostrclient relay endpoint (derived from your Base URL). Leave as-is unless the spire must use a different relay. One per line; blank uses the default."
|
||||||
type="textarea" autogrow
|
type="textarea" autogrow
|
||||||
class="q-mb-md"
|
class="q-mb-md"
|
||||||
dense outlined></q-input>
|
dense outlined></q-input>
|
||||||
|
|
|
||||||
|
|
@ -175,3 +175,15 @@ def test_revoke_failure_maps_to_bad_gateway(monkeypatch):
|
||||||
_call_revoke()
|
_call_revoke()
|
||||||
assert ei.value.status_code == 502
|
assert ei.value.status_code == 502
|
||||||
assert state["unpaired"] is None # not persisted on failure
|
assert state["unpaired"] is None # not persisted on failure
|
||||||
|
|
||||||
|
|
||||||
|
def test_default_relay_endpoint_returns_nostrclient_url():
|
||||||
|
from lnbits.settings import settings
|
||||||
|
|
||||||
|
prev = settings.lnbits_baseurl
|
||||||
|
try:
|
||||||
|
settings.lnbits_baseurl = "https://lnbits.example.com/"
|
||||||
|
result = asyncio.run(views_api.api_default_relay(SimpleNamespace(id="op1")))
|
||||||
|
assert result == {"relay": "wss://lnbits.example.com/nostrclient/api/v1/relay"}
|
||||||
|
finally:
|
||||||
|
settings.lnbits_baseurl = prev
|
||||||
|
|
|
||||||
11
views_api.py
11
views_api.py
|
|
@ -33,6 +33,7 @@ from .pairing import (
|
||||||
PairResult,
|
PairResult,
|
||||||
PairingError,
|
PairingError,
|
||||||
RevokeResult,
|
RevokeResult,
|
||||||
|
default_relay_endpoint,
|
||||||
pair_spire,
|
pair_spire,
|
||||||
revoke_spire,
|
revoke_spire,
|
||||||
)
|
)
|
||||||
|
|
@ -297,6 +298,16 @@ async def api_create_machine(
|
||||||
return machine
|
return machine
|
||||||
|
|
||||||
|
|
||||||
|
@spirekeeper_api_router.get("/api/v1/dca/default-relay")
|
||||||
|
async def api_default_relay(user: User = Depends(check_user_exists)) -> dict:
|
||||||
|
"""The relay a pairing seed defaults to when the operator leaves it blank —
|
||||||
|
this lnbits' nostrclient proxy endpoint, derived from lnbits_baseurl
|
||||||
|
(bitspire#70). The pair dialog pre-fills it. `None` if lnbits_baseurl is
|
||||||
|
unset."""
|
||||||
|
_ = user
|
||||||
|
return {"relay": default_relay_endpoint()}
|
||||||
|
|
||||||
|
|
||||||
@spirekeeper_api_router.post(
|
@spirekeeper_api_router.post(
|
||||||
"/api/v1/dca/machines/{machine_id}/pair", response_model=PairResult
|
"/api/v1/dca/machines/{machine_id}/pair", response_model=PairResult
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue