- Change the title and content of the RelayHubDemo component to reflect its new purpose as Relay Hub Status. - Update routing paths and component names in the Navbar and router configuration to align with the new naming convention. - Enhance user navigation by ensuring consistent terminology across the application.
331 lines
12 KiB
Vue
331 lines
12 KiB
Vue
<template>
|
|
<div class="min-h-screen bg-gray-50 dark:bg-gray-900 py-8">
|
|
<div class="max-w-6xl mx-auto px-4">
|
|
<div class="mb-8">
|
|
<h1 class="text-3xl font-bold text-gray-900 dark:text-white mb-2">Relay Hub Status</h1>
|
|
<p class="text-gray-600 dark:text-gray-300">
|
|
Monitor and test the centralized Nostr relay hub functionality
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Relay Hub Status -->
|
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-6 mb-6">
|
|
<h2 class="text-xl font-semibold text-gray-900 dark:text-white mb-4">Relay Hub Status</h2>
|
|
<RelayHubStatus />
|
|
</div>
|
|
|
|
<!-- Subscription Details -->
|
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-6 mb-6">
|
|
<div class="flex items-center justify-between mb-4">
|
|
<h2 class="text-xl font-semibold text-gray-900 dark:text-white">Subscription Details</h2>
|
|
<button
|
|
@click="toggleSubscriptionDetails"
|
|
class="flex items-center gap-2 px-3 py-2 text-sm bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors"
|
|
>
|
|
<span>{{ showSubscriptionDetails ? 'Hide' : 'Show' }} Details</span>
|
|
<svg
|
|
class="w-4 h-4 transition-transform duration-200"
|
|
:class="{ 'rotate-180': showSubscriptionDetails }"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
viewBox="0 0 24 24"
|
|
>
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
|
|
<div v-if="showSubscriptionDetails">
|
|
<div v-if="subscriptionDetails.length > 0" class="space-y-4">
|
|
<div
|
|
v-for="sub in subscriptionDetails"
|
|
:key="sub.id"
|
|
class="p-4 border border-gray-200 dark:border-gray-600 rounded-lg bg-gray-50 dark:bg-gray-700"
|
|
>
|
|
<div class="font-mono text-sm font-semibold text-blue-600 dark:text-blue-400 mb-2">
|
|
{{ sub.id }}
|
|
</div>
|
|
<div class="text-sm text-gray-600 dark:text-gray-300">
|
|
<div><strong>Filters:</strong> {{ sub.filters.length }} filter(s)</div>
|
|
<div v-if="sub.relays && sub.relays.length > 0">
|
|
<strong>Relays:</strong> {{ sub.relays.join(', ') }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div v-else class="text-gray-500 dark:text-gray-400 text-center py-4">
|
|
No active subscriptions
|
|
</div>
|
|
</div>
|
|
|
|
<div v-else class="text-center py-8">
|
|
<div class="text-gray-400 dark:text-gray-500 text-sm">
|
|
Click "Show Details" to view subscription information
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Connection Test -->
|
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-6 mb-6">
|
|
<h2 class="text-xl font-semibold text-gray-900 dark:text-white mb-4">Connection Test</h2>
|
|
<div class="space-y-4">
|
|
<button
|
|
@click="testConnection"
|
|
:disabled="isTesting || isConnected"
|
|
class="px-4 py-2 bg-blue-600 dark:bg-blue-500 text-white rounded-lg hover:bg-blue-700 dark:hover:bg-blue-600 disabled:bg-gray-400 dark:disabled:bg-gray-600 disabled:cursor-not-allowed transition-colors"
|
|
>
|
|
{{ isTesting ? 'Testing...' : 'Test Connection' }}
|
|
</button>
|
|
|
|
<div v-if="testResult" class="p-4 rounded-lg" :class="{
|
|
'bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-700 text-green-800 dark:text-green-200': testResult.success,
|
|
'bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-700 text-red-800 dark:text-red-200': !testResult.success
|
|
}">
|
|
<p class="font-medium">{{ testResult.message }}</p>
|
|
<div v-if="testResult.success" class="mt-2 text-sm">
|
|
<p>Connected Relays: {{ testResult.connectedCount }}</p>
|
|
<p>Connection Health: {{ testResult.health?.toFixed(1) }}%</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Subscription Test -->
|
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-6 mb-6">
|
|
<h2 class="text-xl font-semibold text-gray-900 dark:text-white mb-4">Subscription Test</h2>
|
|
<div class="space-y-4">
|
|
<button
|
|
@click="testSubscription"
|
|
:disabled="isSubscribing"
|
|
class="px-4 py-2 bg-blue-600 dark:bg-blue-500 text-white rounded-lg hover:bg-blue-700 dark:hover:bg-blue-600 disabled:bg-gray-400 dark:disabled:bg-gray-600 disabled:cursor-not-allowed transition-colors"
|
|
>
|
|
{{ isSubscribing ? 'Subscribing...' : 'Test Subscription' }}
|
|
</button>
|
|
|
|
<button
|
|
v-if="activeTestSubscription"
|
|
@click="unsubscribeTest"
|
|
class="px-4 py-2 bg-red-600 dark:bg-red-500 text-white rounded-lg hover:bg-red-700 dark:hover:bg-red-600 transition-colors"
|
|
>
|
|
Unsubscribe
|
|
</button>
|
|
|
|
<div v-if="subscriptionEvents.length > 0" class="text-sm">
|
|
<p class="font-medium text-gray-900 dark:text-white mb-2">Received Events ({{ subscriptionEvents.length }}):</p>
|
|
<div class="space-y-2 max-h-40 overflow-y-auto">
|
|
<div
|
|
v-for="event in subscriptionEvents.slice(-5)"
|
|
:key="event.id"
|
|
class="p-2 bg-gray-50 dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded text-xs font-mono text-gray-700 dark:text-gray-300"
|
|
>
|
|
{{ event.id }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Mobile Visibility Test -->
|
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-6 mb-6">
|
|
<h2 class="text-xl font-semibold text-gray-900 dark:text-white mb-4">Mobile Visibility Test</h2>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<div class="p-4 border border-gray-200 dark:border-gray-600 rounded-lg bg-gray-50 dark:bg-gray-700">
|
|
<div class="flex items-center space-x-2 mb-2">
|
|
<div class="w-3 h-3 rounded-full" :class="pageVisible ? 'bg-green-500' : 'bg-red-500'"></div>
|
|
<span class="font-medium text-gray-900 dark:text-white">Page Visibility</span>
|
|
</div>
|
|
<p class="text-sm text-gray-600 dark:text-gray-300">
|
|
{{ pageVisible ? 'Page is visible' : 'Page is hidden (backgrounded)' }}
|
|
</p>
|
|
</div>
|
|
|
|
<div class="p-4 border border-gray-200 dark:border-gray-600 rounded-lg bg-gray-50 dark:bg-gray-700">
|
|
<div class="flex items-center space-x-2 mb-2">
|
|
<div class="w-3 h-3 rounded-full" :class="networkOnline ? 'bg-green-500' : 'bg-red-500'"></div>
|
|
<span class="font-medium text-gray-900 dark:text-white">Network Status</span>
|
|
</div>
|
|
<p class="text-sm text-gray-600 dark:text-gray-300">
|
|
{{ networkOnline ? 'Network is online' : 'Network is offline' }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Configuration Info -->
|
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-6">
|
|
<h2 class="text-xl font-semibold text-gray-900 dark:text-white mb-4">Configuration</h2>
|
|
<div class="space-y-2 text-sm">
|
|
<div class="flex justify-between">
|
|
<span class="font-medium text-gray-700 dark:text-gray-300">Configured Relays:</span>
|
|
<span class="text-gray-900 dark:text-white">{{ config.nostr.relays.length }}</span>
|
|
</div>
|
|
<div class="flex justify-between">
|
|
<span class="font-medium text-gray-700 dark:text-gray-300">Market Relays:</span>
|
|
<span class="text-gray-900 dark:text-white">{{ config.market.supportedRelays.length }}</span>
|
|
</div>
|
|
<div class="mt-4">
|
|
<p class="font-medium text-gray-700 dark:text-gray-300 mb-2">Relay URLs:</p>
|
|
<div class="space-y-1">
|
|
<div
|
|
v-for="relay in config.nostr.relays"
|
|
:key="relay"
|
|
class="font-mono text-xs bg-gray-50 dark:bg-gray-700 p-2 rounded border border-gray-200 dark:border-gray-600 text-gray-700 dark:text-gray-300"
|
|
>
|
|
{{ relay }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, onMounted, onUnmounted } from 'vue'
|
|
import RelayHubStatus from '@/components/RelayHubStatus.vue'
|
|
import { useRelayHub } from '@/composables/useRelayHub'
|
|
import { config } from '@/lib/config'
|
|
|
|
const {
|
|
isConnected,
|
|
connectionStatus,
|
|
connectionHealth,
|
|
connectedRelayCount,
|
|
subscriptionDetails,
|
|
initialize,
|
|
connect,
|
|
subscribe
|
|
} = useRelayHub()
|
|
|
|
// Test state
|
|
const isTesting = ref(false)
|
|
const testResult = ref<{
|
|
success: boolean
|
|
message: string
|
|
connectedCount?: number
|
|
health?: number
|
|
} | null>(null)
|
|
|
|
const isSubscribing = ref(false)
|
|
const activeTestSubscription = ref<(() => void) | null>(null)
|
|
const subscriptionEvents = ref<any[]>([])
|
|
|
|
// Mobile visibility state
|
|
const pageVisible = ref(!document.hidden)
|
|
const networkOnline = ref(navigator.onLine)
|
|
|
|
// Subscription details visibility state
|
|
const showSubscriptionDetails = ref(false)
|
|
|
|
const toggleSubscriptionDetails = () => {
|
|
showSubscriptionDetails.value = !showSubscriptionDetails.value
|
|
}
|
|
|
|
// Test connection
|
|
const testConnection = async () => {
|
|
try {
|
|
isTesting.value = true
|
|
testResult.value = null
|
|
|
|
if (!isConnected.value) {
|
|
await connect()
|
|
}
|
|
|
|
testResult.value = {
|
|
success: true,
|
|
message: 'Connection test successful!',
|
|
connectedCount: connectedRelayCount.value,
|
|
health: connectionHealth.value
|
|
}
|
|
} catch (error) {
|
|
testResult.value = {
|
|
success: false,
|
|
message: `Connection test failed: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
}
|
|
} finally {
|
|
isTesting.value = false
|
|
}
|
|
}
|
|
|
|
// Test subscription
|
|
const testSubscription = async () => {
|
|
try {
|
|
isSubscribing.value = true
|
|
subscriptionEvents.value = []
|
|
|
|
// Subscribe to some test events (kind 1 text notes)
|
|
const unsubscribe = subscribe({
|
|
id: 'test-subscription',
|
|
filters: [{ kinds: [1], limit: 10 }],
|
|
onEvent: (event: any) => {
|
|
subscriptionEvents.value.push(event)
|
|
console.log('Received test event:', event)
|
|
},
|
|
onEose: () => {
|
|
console.log('Test subscription EOSE')
|
|
}
|
|
})
|
|
|
|
activeTestSubscription.value = unsubscribe
|
|
|
|
} catch (error) {
|
|
console.error('Subscription test failed:', error)
|
|
} finally {
|
|
isSubscribing.value = false
|
|
}
|
|
}
|
|
|
|
// Unsubscribe from test
|
|
const unsubscribeTest = () => {
|
|
if (activeTestSubscription.value) {
|
|
activeTestSubscription.value()
|
|
activeTestSubscription.value = null
|
|
subscriptionEvents.value = []
|
|
}
|
|
}
|
|
|
|
// Setup mobile visibility handlers
|
|
const handleVisibilityChange = () => {
|
|
pageVisible.value = !document.hidden
|
|
}
|
|
|
|
const handleOnline = () => {
|
|
networkOnline.value = true
|
|
}
|
|
|
|
const handleOffline = () => {
|
|
networkOnline.value = false
|
|
}
|
|
|
|
onMounted(() => {
|
|
// Initialize relay hub if not already done
|
|
if (!isConnected.value && connectionStatus.value === 'disconnected') {
|
|
initialize().catch(console.error)
|
|
}
|
|
|
|
// Setup visibility and network listeners
|
|
document.addEventListener('visibilitychange', handleVisibilityChange)
|
|
window.addEventListener('online', handleOnline)
|
|
window.addEventListener('offline', handleOffline)
|
|
})
|
|
|
|
onUnmounted(() => {
|
|
// Cleanup listeners
|
|
document.removeEventListener('visibilitychange', handleVisibilityChange)
|
|
window.removeEventListener('online', handleOnline)
|
|
window.removeEventListener('offline', handleOffline)
|
|
|
|
// Cleanup test subscription
|
|
if (activeTestSubscription.value) {
|
|
activeTestSubscription.value()
|
|
}
|
|
})
|
|
</script>
|
|
|
|
<style scoped>
|
|
.container {
|
|
min-height: 100vh;
|
|
background-color: #f8fafc;
|
|
}
|
|
</style>
|