fix: improve nostrclient status detection and display

- Call nostrclient /relays API directly from frontend for accurate status
- Show correct error messages from API response (body.detail)
- Add orange warning state for no relays configured
- Show relay count when connected (X of Y connected)
- Simplify status logic: 200 = green, no relays = orange, error = red

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Ben Weeks 2025-12-22 12:49:33 +00:00
parent 9911a03575
commit d2755d7232
4 changed files with 118 additions and 60 deletions

View file

@ -18,21 +18,18 @@ window.app = Vue.createApp({
},
wsConnection: null,
nostrStatus: {
nostrclient_available: false,
nostrclient_relays: [],
nostrclient_error: null,
nostrmarket_running: false,
websocket_connected: false
connected: false,
error: null,
relays_connected: 0,
relays_total: 0
}
}
},
computed: {
nostrStatusColor: function () {
if (!this.nostrStatus.nostrclient_available) {
return 'red'
} else if (this.nostrStatus.websocket_connected) {
if (this.nostrStatus.connected) {
return 'green'
} else if (this.nostrStatus.nostrmarket_running) {
} else if (this.nostrStatus.warning) {
return 'orange'
}
return 'red'
@ -218,31 +215,61 @@ window.app = Vue.createApp({
})
}
},
checkNostrStatus: async function () {
checkNostrStatus: async function (showNotification = false) {
try {
const {data} = await LNbits.api.request(
'GET',
'/nostrmarket/api/v1/status',
this.g.user.wallets[0].inkey
)
this.nostrStatus = data
if (!data.nostrclient_available) {
const response = await fetch('/nostrclient/api/v1/relays')
const body = await response.json()
console.log('Nostrclient /relays:', response.status, body)
if (response.status === 200) {
const relaysConnected = body.filter(r => r.connected).length
if (body.length === 0) {
this.nostrStatus = {
connected: false,
error: 'No relays configured in Nostr Client',
relays_connected: 0,
relays_total: 0,
warning: true
}
} else {
this.nostrStatus = {
connected: true,
error: null,
relays_connected: relaysConnected,
relays_total: body.length
}
}
} else {
this.nostrStatus = {
connected: false,
error: body.detail,
relays_connected: 0,
relays_total: 0
}
}
if (showNotification) {
this.$q.notify({
timeout: 5000,
type: 'warning',
message: 'Nostrclient extension not available',
caption:
data.nostrclient_error ||
'Please install and configure the nostrclient extension'
timeout: 3000,
type: this.nostrStatus.connected ? 'positive' : 'warning',
message: this.nostrStatus.connected ? 'Connected' : 'Disconnected',
caption: this.nostrStatus.error || undefined
})
}
} catch (error) {
console.error('Failed to check nostr status:', error)
this.nostrStatus = {
nostrclient_available: false,
nostrclient_relays: [],
nostrclient_error: 'Failed to check status',
nostrmarket_running: false,
websocket_connected: false
connected: false,
error: error.message,
relays_connected: 0,
relays_total: 0
}
if (showNotification) {
this.$q.notify({
timeout: 5000,
type: 'negative',
message: this.nostrStatus.error
})
}
}
},
@ -253,13 +280,18 @@ window.app = Vue.createApp({
)
.onOk(async () => {
try {
this.$q.notify({
timeout: 2000,
type: 'info',
message: 'Reconnecting...'
})
await LNbits.api.request(
'PUT',
'/nostrmarket/api/v1/restart',
this.g.user.wallets[0].adminkey
)
// Check status after restart
setTimeout(() => this.checkNostrStatus(), 2000)
// Check status after restart (give time for websocket to reconnect)
setTimeout(() => this.checkNostrStatus(true), 3000)
} catch (error) {
LNbits.utils.notifyApiError(error)
}

View file

@ -1,6 +1,6 @@
<q-card>
<q-card-section>
<p class="text-body1">
<p>
Create, edit and publish products to your Nostr relays. Customers can
browse your stalls and pay with Lightning.
</p>

View file

@ -170,7 +170,7 @@
</q-item-label>
</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="checkNostrStatus">
<q-item clickable v-close-popup @click="checkNostrStatus(true)">
<q-item-section avatar>
<q-icon name="wifi_find" color="primary"></q-icon>
</q-item-section>
@ -185,27 +185,29 @@
<q-item>
<q-item-section>
<q-item-label caption>
<strong>Nostrclient:</strong>
<strong>Status:</strong>
<q-badge
:color="nostrStatus.nostrclient_available ? 'green' : 'red'"
:color="nostrStatus.connected ? 'green' : 'red'"
class="q-ml-xs"
v-text="nostrStatus.nostrclient_available ? 'Available' : 'Not Available'"
></q-badge>
<br />
<strong>Relays:</strong>
<span v-text="(nostrStatus.nostrclient_relays || []).length"></span> configured<br />
<strong>WebSocket:</strong>
<q-badge
:color="nostrStatus.websocket_connected ? 'green' : 'orange'"
class="q-ml-xs"
v-text="nostrStatus.websocket_connected ? 'Connected' : 'Disconnected'"
v-text="nostrStatus.connected ? 'Connected' : 'Disconnected'"
></q-badge>
</q-item-label>
<q-item-label
v-if="nostrStatus.nostrclient_error"
v-if="nostrStatus.relays_total > 0"
caption
class="q-mt-xs"
>
<strong>Relays:</strong>&nbsp;
<span v-text="nostrStatus.relays_connected"></span>
of
<span v-text="nostrStatus.relays_total"></span>
connected
</q-item-label>
<q-item-label
v-if="nostrStatus.error"
caption
class="text-negative q-mt-xs"
v-text="nostrStatus.nostrclient_error"
v-text="nostrStatus.error"
></q-item-label>
</q-item-section>
</q-item>

View file

@ -1113,31 +1113,55 @@ async def api_get_nostr_status(
) -> dict:
"""Get the status of the nostrclient extension."""
nostrclient_available = False
nostrclient_relays = []
nostrclient_error = None
relays = []
error = None
try:
async with httpx.AsyncClient() as client:
response = await client.get(
f"http://localhost:{settings.port}/nostrclient/api/v1/relays",
timeout=5.0,
url = f"http://127.0.0.1:{settings.port}/nostrclient/api/v1/relays"
logger.info(f"Calling nostrclient API: {url}")
response = await client.get(url, timeout=5.0)
logger.info(
f"Nostrclient response: status={response.status_code}, "
f"body={response.text[:500]}"
)
if response.status_code == 200:
nostrclient_available = True
nostrclient_relays = response.json()
relays = response.json()
else:
# Any non-200 response means we can't verify nostrclient is working
try:
error = response.json().get("detail", f"HTTP {response.status_code}")
except Exception:
error = f"HTTP {response.status_code}"
except httpx.ConnectError:
nostrclient_error = "Cannot connect to nostrclient extension"
error = "Cannot connect to nostrclient extension"
except httpx.TimeoutException:
nostrclient_error = "Timeout connecting to nostrclient"
error = "Timeout connecting to nostrclient"
except Exception as ex:
nostrclient_error = str(ex)
error = str(ex)
# Only show connected if no errors and websocket is connected
connected = (
nostrclient_available
and nostr_client.is_websocket_connected
and error is None
)
# If nostrclient exists but websocket not connected, explain why
if nostrclient_available and not nostr_client.is_websocket_connected and not error:
error = "Websocket not connected"
# Count connected relays
relays_connected = sum(1 for r in relays if r.get("connected", False))
relays_total = len(relays)
return {
"nostrclient_available": nostrclient_available,
"nostrclient_relays": nostrclient_relays,
"nostrclient_error": nostrclient_error,
"nostrmarket_running": nostr_client.running,
"websocket_connected": nostr_client.is_websocket_connected,
"connected": connected,
"error": error,
"relays_connected": relays_connected,
"relays_total": relays_total,
}