Remove LEGACY Nostr client services and related components to streamline the codebase

- Delete Nostr client hub and associated files, including the Nostr store, to eliminate unused functionality.
- Update service tokens and dependency injections to reflect the removal of Nostr client services.
- Adjust notification settings in NotificationSettings.vue to prepare for future implementation of notifications.
This commit is contained in:
padreug 2025-09-06 10:55:10 +02:00
parent 92c33aa0a3
commit 7e4b64b831
8 changed files with 7 additions and 661 deletions

View file

@ -78,7 +78,6 @@ const relayHub = injectService(SERVICE_TOKENS.RELAY_HUB)
**Available Services:**
- `SERVICE_TOKENS.RELAY_HUB` - Centralized Nostr relay management
- `SERVICE_TOKENS.NOSTR_CLIENT_HUB` - Nostr client services
- `SERVICE_TOKENS.AUTH_SERVICE` - Authentication services
- `SERVICE_TOKENS.VISIBILITY_SERVICE` - App visibility and connection management

View file

@ -151,15 +151,13 @@ import { Button } from '@/components/ui/button'
import { Label } from '@/components/ui/label'
import { Badge } from '@/components/ui/badge'
import { Bell, BellOff, TestTube, AlertCircle } from 'lucide-vue-next'
import { useNostrStore } from '@/stores/nostr'
import { notificationManager } from '@/lib/notifications/manager'
import { pushService } from '@/lib/notifications/push'
import { configUtils } from '@/lib/config'
import { toast } from 'vue-sonner'
import { storeToRefs } from 'pinia'
const nostrStore = useNostrStore()
const { notificationsEnabled } = storeToRefs(nostrStore)
// Placeholder for notifications - will be implemented later
const notificationsEnabled = ref(false)
const isLoading = ref(false)
const isTestLoading = ref(false)
@ -174,14 +172,14 @@ onMounted(async () => {
async function checkStatus() {
isBlocked.value = pushService.getPermission() === 'denied'
await nostrStore.checkPushNotificationStatus()
// TODO: Implement push notification status checking when notifications are re-added
}
async function enableNotifications() {
try {
isLoading.value = true
await nostrStore.enablePushNotifications()
toast.success('Push notifications enabled!')
// TODO: Implement push notification enabling when notifications are re-added
toast.success('Push notifications will be implemented later')
await checkStatus()
} catch (error) {
const message = error instanceof Error ? error.message : 'Failed to enable notifications'
@ -193,7 +191,7 @@ async function enableNotifications() {
async function disableNotifications() {
try {
await nostrStore.disablePushNotifications()
// TODO: Implement push notification disabling when notifications are re-added
toast.success('Push notifications disabled')
await checkStatus()
} catch (error) {

View file

@ -44,7 +44,6 @@ export abstract class BaseService {
// Core dependencies with proper typing
protected relayHub: any = null
protected authService: any = null
protected nostrClientHub: any = null
protected visibilityService: any = null
// Service state
@ -134,7 +133,6 @@ export abstract class BaseService {
// Try to inject core dependencies
this.relayHub = tryInjectService(SERVICE_TOKENS.RELAY_HUB)
this.authService = tryInjectService(SERVICE_TOKENS.AUTH_SERVICE)
this.nostrClientHub = tryInjectService(SERVICE_TOKENS.NOSTR_CLIENT_HUB)
this.visibilityService = tryInjectService(SERVICE_TOKENS.VISIBILITY_SERVICE)
// Check if all required dependencies are available
@ -180,9 +178,6 @@ export abstract class BaseService {
if (deps.includes('AuthService') && !this.authService) {
missing.push('AuthService')
}
if (deps.includes('NostrClientHub') && !this.nostrClientHub) {
missing.push('NostrClientHub')
}
if (deps.includes('VisibilityService') && !this.visibilityService) {
missing.push('VisibilityService')
}
@ -269,7 +264,6 @@ export abstract class BaseService {
this.isInitialized.value = false
this.relayHub = null
this.authService = null
this.nostrClientHub = null
console.log(`♻️ ${this.metadata.name} disposed`)

View file

@ -109,7 +109,6 @@ export const SERVICE_TOKENS = {
// Nostr services
RELAY_HUB: Symbol('relayHub'),
NOSTR_CLIENT_HUB: Symbol('nostrClientHub'),
// Auth services
AUTH_SERVICE: Symbol('authService'),

View file

@ -2,7 +2,6 @@ import type { App } from 'vue'
import type { ModulePlugin } from '@/core/types'
import { container, SERVICE_TOKENS } from '@/core/di-container'
import { relayHub } from './nostr/relay-hub'
import { nostrclientHub } from './nostr/nostrclient-hub'
// Import auth services
import { auth } from './auth/auth-service'
@ -27,7 +26,6 @@ export const baseModule: ModulePlugin = {
// Register core Nostr services
container.provide(SERVICE_TOKENS.RELAY_HUB, relayHub)
container.provide(SERVICE_TOKENS.NOSTR_CLIENT_HUB, nostrclientHub)
// Register auth service
container.provide(SERVICE_TOKENS.AUTH_SERVICE, auth)
@ -56,10 +54,6 @@ export const baseModule: ModulePlugin = {
waitForDependencies: false, // VisibilityService has no dependencies
maxRetries: 1
})
await nostrclientHub.initialize({
waitForDependencies: true, // NostrClientHub depends on VisibilityService
maxRetries: 3
})
console.log('✅ Base module installed successfully')
},
@ -69,7 +63,6 @@ export const baseModule: ModulePlugin = {
// Cleanup services
await relayHub.dispose()
await nostrclientHub.dispose()
await auth.dispose()
await paymentService.dispose()
await visibilityService.dispose()
@ -79,7 +72,6 @@ export const baseModule: ModulePlugin = {
services: {
relayHub,
nostrclientHub,
auth,
paymentService,
pwaService

View file

@ -1,16 +1,10 @@
// Re-export Nostr infrastructure from base module
export { RelayHub } from './relay-hub'
export { NostrclientHub } from './nostrclient-hub'
export { relayHub } from './relay-hub'
export { nostrclientHub } from './nostrclient-hub'
// Re-export types
export type {
RelayConfig,
SubscriptionConfig,
RelayStatus
} from './relay-hub'
export type {
NostrclientConfig
} from './nostrclient-hub'
} from './relay-hub'

View file

@ -1,442 +0,0 @@
import type { Filter, Event } from 'nostr-tools'
import { BaseService } from '@/core/base/BaseService'
export interface NostrclientConfig {
url: string
privateKey?: string // For private WebSocket endpoint
}
export interface SubscriptionConfig {
id: string
filters: Filter[]
onEvent?: (event: Event) => void
onEose?: () => void
onClose?: () => void
}
export interface RelayStatus {
url: string
connected: boolean
lastSeen: number
error?: string
}
export class NostrclientHub extends BaseService {
// Service metadata
protected readonly metadata = {
name: 'NostrclientHub',
version: '1.0.0',
dependencies: ['VisibilityService']
}
// EventEmitter functionality
private events: { [key: string]: Function[] } = {}
// Service state
private ws: WebSocket | null = null
private config: NostrclientConfig
private subscriptions: Map<string, SubscriptionConfig> = new Map()
private reconnectInterval?: number
private reconnectAttempts = 0
private readonly maxReconnectAttempts = 5
private readonly reconnectDelay = 5000
private visibilityUnsubscribe?: () => void
// Connection state
private _isConnected = false
private _isConnecting = false
constructor(config: NostrclientConfig) {
super()
this.config = config
}
// EventEmitter methods
on(event: string, listener: Function) {
if (!this.events[event]) {
this.events[event] = []
}
this.events[event].push(listener)
}
emit(event: string, ...args: any[]) {
if (this.events[event]) {
this.events[event].forEach(listener => listener(...args))
}
}
removeAllListeners(event?: string) {
if (event) {
delete this.events[event]
} else {
this.events = {}
}
}
/**
* Service-specific initialization (called by BaseService)
*/
protected async onInitialize(): Promise<void> {
// Connect to WebSocket
console.log('🔧 NostrclientHub: Initializing connection to', this.config.url)
await this.connect()
// Register with visibility service
this.registerWithVisibilityService()
this.debug('NostrclientHub initialized')
}
get isConnected(): boolean {
return this._isConnected
}
get isConnecting(): boolean {
return this._isConnecting
}
get totalSubscriptionCount(): number {
return this.subscriptions.size
}
get subscriptionDetails(): Array<{ id: string; filters: Filter[] }> {
return Array.from(this.subscriptions.values()).map(sub => ({
id: sub.id,
filters: sub.filters
}))
}
/**
* Connect to the nostrclient WebSocket
*/
async connect(): Promise<void> {
if (this._isConnecting || this._isConnected) {
return
}
this._isConnecting = true
this.reconnectAttempts++
try {
console.log('🔧 NostrclientHub: Connecting to nostrclient WebSocket')
// Determine WebSocket endpoint
const wsUrl = this.config.privateKey
? `${this.config.url}/${this.config.privateKey}` // Private endpoint
: `${this.config.url}/relay` // Public endpoint
this.ws = new WebSocket(wsUrl)
this.ws.onopen = () => {
console.log('🔧 NostrclientHub: WebSocket connected')
this._isConnected = true
this._isConnecting = false
this.reconnectAttempts = 0
this.emit('connected')
// Resubscribe to existing subscriptions
this.resubscribeAll()
}
this.ws.onmessage = (event) => {
this.handleMessage(event.data)
}
this.ws.onclose = (event) => {
console.log('🔧 NostrclientHub: WebSocket closed:', event.code, event.reason)
this._isConnected = false
this._isConnecting = false
this.emit('disconnected', event)
// Schedule reconnection
if (this.reconnectAttempts < this.maxReconnectAttempts) {
this.scheduleReconnect()
} else {
this.emit('maxReconnectionAttemptsReached')
}
}
this.ws.onerror = (error) => {
console.error('🔧 NostrclientHub: WebSocket error:', error)
this.emit('error', error)
}
} catch (error) {
this._isConnecting = false
console.error('🔧 NostrclientHub: Connection failed:', error)
this.emit('connectionError', error)
if (this.reconnectAttempts < this.maxReconnectAttempts) {
this.scheduleReconnect()
}
}
}
/**
* Disconnect from the WebSocket
*/
disconnect(): void {
if (this.reconnectInterval) {
clearTimeout(this.reconnectInterval)
this.reconnectInterval = undefined
}
if (this.ws) {
this.ws.close()
this.ws = null
}
this._isConnected = false
this._isConnecting = false
this.subscriptions.clear()
this.emit('disconnected')
}
/**
* Subscribe to events
*/
subscribe(config: SubscriptionConfig): () => void {
if (!this._isConnected) {
throw new Error('Not connected to nostrclient')
}
// Store subscription
this.subscriptions.set(config.id, config)
// Send REQ message
const reqMessage = JSON.stringify([
'REQ',
config.id,
...config.filters
])
this.ws?.send(reqMessage)
console.log('🔧 NostrclientHub: Subscribed to', config.id)
// Return unsubscribe function
return () => {
this.unsubscribe(config.id)
}
}
/**
* Unsubscribe from events
*/
unsubscribe(subscriptionId: string): void {
if (!this._isConnected) {
return
}
// Send CLOSE message
const closeMessage = JSON.stringify(['CLOSE', subscriptionId])
this.ws?.send(closeMessage)
// Remove from subscriptions
this.subscriptions.delete(subscriptionId)
console.log('🔧 NostrclientHub: Unsubscribed from', subscriptionId)
}
/**
* Publish an event
*/
async publishEvent(event: Event): Promise<void> {
if (!this._isConnected) {
throw new Error('Not connected to nostrclient')
}
const eventMessage = JSON.stringify(['EVENT', event])
this.ws?.send(eventMessage)
console.log('🔧 NostrclientHub: Published event', event.id)
this.emit('eventPublished', { eventId: event.id })
}
/**
* Query events (one-time fetch)
*/
async queryEvents(filters: Filter[]): Promise<Event[]> {
return new Promise((resolve, reject) => {
if (!this._isConnected) {
reject(new Error('Not connected to nostrclient'))
return
}
const queryId = `query-${Date.now()}`
const events: Event[] = []
let eoseReceived = false
// Create temporary subscription for query
const tempSubscription = this.subscribe({
id: queryId,
filters,
onEvent: (event) => {
events.push(event)
},
onEose: () => {
eoseReceived = true
this.unsubscribe(queryId)
resolve(events)
},
onClose: () => {
if (!eoseReceived) {
reject(new Error('Query subscription closed unexpectedly'))
}
}
})
// Timeout after 30 seconds
setTimeout(() => {
if (!eoseReceived) {
tempSubscription()
reject(new Error('Query timeout'))
}
}, 30000)
})
}
/**
* Handle incoming WebSocket messages
*/
private handleMessage(data: string): void {
try {
const message = JSON.parse(data)
if (Array.isArray(message) && message.length >= 2) {
const [type, subscriptionId, ...rest] = message
switch (type) {
case 'EVENT':
const event = rest[0] as Event
const subscription = this.subscriptions.get(subscriptionId)
if (subscription?.onEvent) {
subscription.onEvent(event)
}
this.emit('event', { subscriptionId, event })
break
case 'EOSE':
const eoseSubscription = this.subscriptions.get(subscriptionId)
if (eoseSubscription?.onEose) {
eoseSubscription.onEose()
}
this.emit('eose', { subscriptionId })
break
case 'NOTICE':
console.log('🔧 NostrclientHub: Notice from relay:', rest[0])
this.emit('notice', { message: rest[0] })
break
default:
console.log('🔧 NostrclientHub: Unknown message type:', type)
}
}
} catch (error) {
console.error('🔧 NostrclientHub: Failed to parse message:', error)
}
}
/**
* Resubscribe to all existing subscriptions after reconnection
*/
private resubscribeAll(): void {
for (const [id, config] of this.subscriptions) {
const reqMessage = JSON.stringify([
'REQ',
id,
...config.filters
])
this.ws?.send(reqMessage)
}
console.log('🔧 NostrclientHub: Resubscribed to', this.subscriptions.size, 'subscriptions')
}
/**
* Schedule automatic reconnection
*/
private scheduleReconnect(): void {
if (this.reconnectInterval) {
clearTimeout(this.reconnectInterval)
}
const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1)
console.log(`🔧 NostrclientHub: Scheduling reconnection in ${delay}ms`)
this.reconnectInterval = setTimeout(async () => {
await this.connect()
}, delay) as unknown as number
}
/**
* Register with VisibilityService for connection management
*/
private registerWithVisibilityService(): void {
if (!this.visibilityService) {
this.debug('VisibilityService not available')
return
}
this.visibilityUnsubscribe = this.visibilityService.registerService(
this.metadata.name,
async () => this.handleAppResume(),
async () => this.handleAppPause()
)
this.debug('Registered with VisibilityService')
}
/**
* Handle app resuming from visibility change
*/
private async handleAppResume(): Promise<void> {
this.debug('App resumed - checking nostrclient WebSocket connection')
// Check if we need to reconnect
if (!this.isConnected && !this._isConnecting) {
this.debug('WebSocket disconnected, attempting to reconnect...')
await this.connect()
} else if (this.isConnected) {
// Connection is alive, resubscribe to ensure all subscriptions are active
this.resubscribeAll()
}
}
/**
* Handle app pausing from visibility change
*/
private async handleAppPause(): Promise<void> {
this.debug('App paused - WebSocket connection will be maintained for quick resume')
// Don't immediately disconnect - WebSocket will be checked on resume
// This allows for quick resume without full reconnection overhead
}
/**
* Cleanup when service is disposed (overrides BaseService)
*/
protected async onDispose(): Promise<void> {
// Unregister from visibility service
if (this.visibilityUnsubscribe) {
this.visibilityUnsubscribe()
this.visibilityUnsubscribe = undefined
}
// Disconnect WebSocket
this.disconnect()
// Clear all event listeners
this.removeAllListeners()
this.debug('NostrclientHub disposed')
}
}
// Export singleton instance
export const nostrclientHub = new NostrclientHub({
url: import.meta.env.VITE_NOSTRCLIENT_URL || 'wss://localhost:5000/nostrclient/api/v1'
})
// Ensure global export
;(globalThis as any).nostrclientHub = nostrclientHub

View file

@ -1,188 +0,0 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
import { relayHub } from '@/lib/nostr/relayHub'
import { config } from '@/lib/config'
import { pushService, type PushSubscriptionData } from '@/lib/notifications/push'
// Define an interface for the account object
interface NostrAccount {
privkey: string
pubkey: string
}
export const useNostrStore = defineStore('nostr', () => {
// Connection state
const isConnected = ref(false)
const isConnecting = ref(false)
const error = ref<Error | null>(null)
// Configuration
const relayUrls = ref<string[]>(config.nostr.relays)
const account = ref<NostrAccount | null>(null)
// Push notifications
const pushSubscription = ref<PushSubscriptionData | null>(null)
const notificationsEnabled = ref(false)
// Connection management
async function connect(): Promise<void> {
try {
error.value = null
isConnecting.value = true
// Initialize and connect using the centralized relay hub
await relayHub.initialize(relayUrls.value)
await relayHub.connect()
isConnected.value = relayHub.isConnected
} catch (err) {
error.value = err instanceof Error ? err : new Error('Failed to connect')
isConnected.value = false
throw err
} finally {
isConnecting.value = false
}
}
function disconnect(): void {
// Don't disconnect the relay hub as it's managed centrally
// Just update our local state
isConnected.value = false
isConnecting.value = false
error.value = null
}
// Configuration setters
function setConnected(value: boolean) {
isConnected.value = value
}
function setRelayUrls(urls: string[]) {
relayUrls.value = urls
// The relay hub will handle reconnection with new relays if needed
}
function setAccount(nostrAccount: NostrAccount | null) {
account.value = nostrAccount
}
// Push notification management
async function enablePushNotifications(): Promise<PushSubscriptionData> {
try {
const subscription = await pushService.subscribe()
pushSubscription.value = subscription
notificationsEnabled.value = true
// Store subscription in localStorage for persistence
localStorage.setItem('push-subscription', JSON.stringify(subscription))
localStorage.setItem('notifications-enabled', 'true')
return subscription
} catch (error) {
console.error('Failed to enable push notifications:', error)
throw error
}
}
async function disablePushNotifications(): Promise<void> {
try {
await pushService.unsubscribe()
pushSubscription.value = null
notificationsEnabled.value = false
// Remove from localStorage
localStorage.removeItem('push-subscription')
localStorage.removeItem('notifications-enabled')
} catch (error) {
console.error('Failed to disable push notifications:', error)
}
}
async function checkPushNotificationStatus(): Promise<void> {
try {
// Check localStorage first
const storedEnabled = localStorage.getItem('notifications-enabled') === 'true'
const storedSubscription = localStorage.getItem('push-subscription')
if (storedEnabled && storedSubscription) {
pushSubscription.value = JSON.parse(storedSubscription)
notificationsEnabled.value = true
}
// Verify with push service
const currentSubscription = await pushService.getSubscription()
if (currentSubscription) {
pushSubscription.value = currentSubscription
notificationsEnabled.value = true
} else if (storedEnabled) {
// Stored state says enabled but no actual subscription - clear stored state
await disablePushNotifications()
}
} catch (error) {
console.error('Failed to check push notification status:', error)
}
}
// Send test notification
async function sendTestNotification(): Promise<void> {
await pushService.showLocalNotification({
title: '🚨 Test Admin Announcement',
body: 'This is a test notification to verify push notifications are working correctly.',
icon: '/pwa-192x192.png',
tag: 'test-notification',
data: {
url: '/',
type: 'admin-announcement'
}
})
}
// Setup relay hub event listeners to keep store state in sync
function setupRelayHubListeners(): void {
relayHub.on('connected', () => {
isConnected.value = true
isConnecting.value = false
error.value = null
})
relayHub.on('disconnected', () => {
isConnected.value = false
isConnecting.value = false
})
relayHub.on('error', (err: Error) => {
error.value = err
isConnected.value = false
isConnecting.value = false
})
relayHub.on('connecting', () => {
isConnecting.value = true
})
}
// Initialize relay hub listeners
setupRelayHubListeners()
return {
// State
isConnected,
isConnecting,
error,
relayUrls,
account,
pushSubscription,
notificationsEnabled,
// Actions
connect,
disconnect,
setConnected,
setRelayUrls,
setAccount,
enablePushNotifications,
disablePushNotifications,
checkPushNotificationStatus,
sendTestNotification,
}
})