feat: Add Central Relay Hub Architecture documentation
- Introduce a comprehensive documentation file detailing the Central Relay Hub architecture for managing Nostr relay connections in the Ario application. - Outline key components, integration patterns, benefits, and best practices for utilizing the relay hub, enhancing developer understanding and facilitating easier implementation.
This commit is contained in:
parent
4f2cf7885d
commit
54860d1e2f
1 changed files with 356 additions and 0 deletions
356
RELAY_HUB_ARCHITECTURE.md
Normal file
356
RELAY_HUB_ARCHITECTURE.md
Normal file
|
|
@ -0,0 +1,356 @@
|
|||
# Central Relay Hub Architecture
|
||||
|
||||
## Overview
|
||||
|
||||
The Ario application uses a centralized relay hub architecture to manage all Nostr relay connections. This design provides a single point of connection management, efficient resource sharing, and consistent relay behavior across all components.
|
||||
|
||||
## Architecture Components
|
||||
|
||||
### 1. Core Relay Hub (`relayHub.ts`)
|
||||
|
||||
The `RelayHub` class is the foundation of the relay management system, built on top of `nostr-tools` SimplePool.
|
||||
|
||||
**Key Features:**
|
||||
- **Singleton Pattern**: Single instance manages all relay connections
|
||||
- **Connection Pooling**: Efficiently manages multiple relay connections
|
||||
- **Automatic Reconnection**: Handles connection failures and reconnection logic
|
||||
- **Health Monitoring**: Regular health checks and status reporting
|
||||
- **Mobile Optimization**: Handles mobile app visibility changes
|
||||
- **Event Emission**: Provides event-driven architecture for status updates
|
||||
|
||||
**Location:** `src/lib/nostr/relayHub.ts`
|
||||
|
||||
### 2. Relay Hub Composable (`useRelayHub.ts`)
|
||||
|
||||
A Vue 3 composable that provides a reactive interface to the relay hub.
|
||||
|
||||
**Key Features:**
|
||||
- **Reactive State**: `isConnected`, `connectionStatus`, `relayStatuses`
|
||||
- **Connection Management**: `initialize()`, `connect()`, `disconnect()`
|
||||
- **Subscription Management**: `subscribe()`, `unsubscribe()`
|
||||
- **Event Publishing**: `publishEvent()`, `queryEvents()`
|
||||
- **Health Monitoring**: Connection health percentages and status
|
||||
|
||||
**Location:** `src/composables/useRelayHub.ts`
|
||||
|
||||
### 3. Application Integration (`App.vue`)
|
||||
|
||||
The main application component initializes the relay hub at startup.
|
||||
|
||||
**Key Integration Points:**
|
||||
```typescript
|
||||
// Initialize relay hub
|
||||
const relayHub = useRelayHub()
|
||||
|
||||
onMounted(async () => {
|
||||
// Initialize relay hub
|
||||
try {
|
||||
await relayHub.initialize()
|
||||
console.log('Relay hub initialized successfully')
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize relay hub:', error)
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## How Components Plug Into the Relay Hub
|
||||
|
||||
### 1. Direct Integration via `useRelayHub()`
|
||||
|
||||
Components can directly use the relay hub composable:
|
||||
|
||||
```typescript
|
||||
import { useRelayHub } from '@/composables/useRelayHub'
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const relayHub = useRelayHub()
|
||||
|
||||
// Access reactive state
|
||||
const isConnected = relayHub.isConnected
|
||||
const connectionStatus = relayHub.connectionStatus
|
||||
|
||||
// Use connection methods
|
||||
const connect = async () => {
|
||||
await relayHub.connect()
|
||||
}
|
||||
|
||||
// Subscribe to events
|
||||
const unsubscribe = relayHub.subscribe({
|
||||
id: 'my-subscription',
|
||||
filters: [{ kinds: [1] }], // Text notes
|
||||
onEvent: (event) => {
|
||||
console.log('Received event:', event)
|
||||
}
|
||||
})
|
||||
|
||||
// Clean up on unmount
|
||||
onUnmounted(() => {
|
||||
unsubscribe()
|
||||
})
|
||||
|
||||
return {
|
||||
isConnected,
|
||||
connectionStatus,
|
||||
connect
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Indirect Integration via Specialized Composables
|
||||
|
||||
Components can use specialized composables that internally use the relay hub:
|
||||
|
||||
#### Chat Integration (`useNostrChat.ts`)
|
||||
|
||||
The chat system integrates with the relay hub for message handling:
|
||||
|
||||
```typescript
|
||||
// Inside useNostrChat.ts
|
||||
const subscribeToPeer = async (peerPubkey: string) => {
|
||||
// Check connection status
|
||||
if (!relayHub.isConnected.value) {
|
||||
await relayHub.connect()
|
||||
}
|
||||
|
||||
// Subscribe to peer messages
|
||||
const unsubscribe = relayHub.subscribe({
|
||||
id: `peer-${peerPubkey}`,
|
||||
filters: [
|
||||
{
|
||||
kinds: [4], // Direct messages
|
||||
'#p': [currentUser.value.pubkey],
|
||||
authors: [peerPubkey]
|
||||
}
|
||||
],
|
||||
onEvent: (event) => handleIncomingMessage(event, peerPubkey)
|
||||
})
|
||||
|
||||
return unsubscribe
|
||||
}
|
||||
```
|
||||
|
||||
#### Market Integration (`useMarket.ts`)
|
||||
|
||||
The market system can integrate similarly:
|
||||
|
||||
```typescript
|
||||
// Example market integration
|
||||
const subscribeToMarketEvents = async () => {
|
||||
if (!relayHub.isConnected.value) {
|
||||
await relayHub.connect()
|
||||
}
|
||||
|
||||
return relayHub.subscribe({
|
||||
id: 'market-events',
|
||||
filters: [
|
||||
{
|
||||
kinds: [30000], // Custom market events
|
||||
'#t': ['market']
|
||||
}
|
||||
],
|
||||
onEvent: (event) => handleMarketEvent(event)
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Configuration and Setup
|
||||
|
||||
#### Relay Configuration
|
||||
|
||||
Relays are configured in the application config:
|
||||
|
||||
```typescript
|
||||
// src/lib/config.ts
|
||||
export const config = {
|
||||
nostr: {
|
||||
relays: [
|
||||
'wss://relay.ariege.io',
|
||||
'wss://lnbits.ariege.io/nostrrelay/benac'
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Initialization Order
|
||||
|
||||
1. **App.vue** initializes the relay hub
|
||||
2. **Components** can use the relay hub immediately after initialization
|
||||
3. **Specialized composables** handle their own relay hub integration
|
||||
|
||||
## Benefits of the Central Relay Hub Architecture
|
||||
|
||||
### 1. **Resource Efficiency**
|
||||
- Single connection pool for all relay connections
|
||||
- Shared connection management across components
|
||||
- Reduced memory usage and connection overhead
|
||||
|
||||
### 2. **Consistent Behavior**
|
||||
- Unified connection status across the application
|
||||
- Consistent error handling and reconnection logic
|
||||
- Standardized relay health monitoring
|
||||
|
||||
### 3. **Scalability**
|
||||
- Easy to add new relay endpoints
|
||||
- Centralized configuration management
|
||||
- Simple to extend with new relay features
|
||||
|
||||
### 4. **Maintainability**
|
||||
- Single point of truth for relay logic
|
||||
- Easier debugging and monitoring
|
||||
- Centralized relay-related bug fixes
|
||||
|
||||
## Best Practices for Component Integration
|
||||
|
||||
### 1. **Always Check Connection Status**
|
||||
|
||||
```typescript
|
||||
const myFunction = async () => {
|
||||
if (!relayHub.isConnected.value) {
|
||||
await relayHub.connect()
|
||||
}
|
||||
// Proceed with relay operations
|
||||
}
|
||||
```
|
||||
|
||||
### 2. **Use Subscription IDs**
|
||||
|
||||
```typescript
|
||||
const subscriptionId = `my-component-${Date.now()}`
|
||||
const unsubscribe = relayHub.subscribe({
|
||||
id: subscriptionId,
|
||||
filters: [...],
|
||||
onEvent: handleEvent
|
||||
})
|
||||
```
|
||||
|
||||
### 3. **Clean Up Subscriptions**
|
||||
|
||||
```typescript
|
||||
onUnmounted(() => {
|
||||
if (unsubscribe) {
|
||||
unsubscribe()
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### 4. **Handle Connection Errors**
|
||||
|
||||
```typescript
|
||||
const connect = async () => {
|
||||
try {
|
||||
await relayHub.connect()
|
||||
} catch (error) {
|
||||
console.error('Failed to connect:', error)
|
||||
// Handle error appropriately
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5. **Monitor Connection Health**
|
||||
|
||||
```typescript
|
||||
const connectionHealth = computed(() => relayHub.connectionHealth)
|
||||
const isHealthy = computed(() => connectionHealth.value > 50)
|
||||
```
|
||||
|
||||
## Example: Creating a New Relay-Enabled Component
|
||||
|
||||
Here's a complete example of a new component that uses the relay hub:
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div>
|
||||
<div>Connection Status: {{ connectionStatus }}</div>
|
||||
<div>Connected Relays: {{ connectedRelayCount }}/{{ totalRelayCount }}</div>
|
||||
<button @click="subscribeToEvents" :disabled="!isConnected">
|
||||
Subscribe to Events
|
||||
</button>
|
||||
<div v-if="events.length">
|
||||
<h3>Received Events:</h3>
|
||||
<div v-for="event in events" :key="event.id">
|
||||
{{ event.content }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
import { useRelayHub } from '@/composables/useRelayHub'
|
||||
|
||||
const relayHub = useRelayHub()
|
||||
const events = ref<any[]>([])
|
||||
let unsubscribe: (() => void) | null = null
|
||||
|
||||
const { isConnected, connectionStatus, connectedRelayCount, totalRelayCount } = relayHub
|
||||
|
||||
const subscribeToEvents = async () => {
|
||||
if (!relayHub.isConnected.value) {
|
||||
await relayHub.connect()
|
||||
}
|
||||
|
||||
unsubscribe = relayHub.subscribe({
|
||||
id: 'my-component-events',
|
||||
filters: [
|
||||
{
|
||||
kinds: [1], // Text notes
|
||||
limit: 10
|
||||
}
|
||||
],
|
||||
onEvent: (event) => {
|
||||
events.value.push(event)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
if (unsubscribe) {
|
||||
unsubscribe()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **Connection Not Established**
|
||||
- Check if relay hub is initialized in App.vue
|
||||
- Verify relay URLs in configuration
|
||||
- Check browser console for connection errors
|
||||
|
||||
2. **Subscriptions Not Working**
|
||||
- Ensure connection is established before subscribing
|
||||
- Verify subscription filters are correct
|
||||
- Check if unsubscribe function is called prematurely
|
||||
|
||||
3. **Performance Issues**
|
||||
- Monitor number of active subscriptions
|
||||
- Use appropriate filter limits
|
||||
- Clean up unused subscriptions
|
||||
|
||||
### Debug Tools
|
||||
|
||||
The relay hub provides several debugging capabilities:
|
||||
|
||||
```typescript
|
||||
// Check connection status
|
||||
console.log('Connected:', relayHub.isConnected.value)
|
||||
console.log('Relay count:', relayHub.totalRelayCount)
|
||||
|
||||
// Monitor relay health
|
||||
console.log('Health:', relayHub.connectionHealth.value)
|
||||
|
||||
// Check specific relay status
|
||||
const status = relayHub.getRelayStatus('wss://relay.example.com')
|
||||
console.log('Relay status:', status)
|
||||
```
|
||||
|
||||
## Conclusion
|
||||
|
||||
The central relay hub architecture provides a robust, efficient, and maintainable way to manage Nostr relay connections across the Ario application. By following the integration patterns outlined in this document, components can easily leverage the relay hub's capabilities while maintaining clean separation of concerns and optimal performance.
|
||||
|
||||
For questions or issues with relay hub integration, refer to the existing implementations in `useNostrChat.ts` and `useMarket.ts` as reference examples.
|
||||
Loading…
Add table
Add a link
Reference in a new issue