Refactor services to extend BaseService for improved structure and dependency management
- Update AuthService and RelayHub to extend BaseService, introducing standardized initialization and metadata handling. - Implement service-specific initialization methods in both services, enhancing error handling and logging. - Modify NostrmarketService to inherit from BaseService, ensuring consistent dependency management and initialization. - Refactor market module to dynamically import NostrmarketService, improving service registration and initialization flow. - Enhance debug logging across services for better traceability during initialization and operation.
This commit is contained in:
parent
8d4c389f71
commit
dc4da570a7
5 changed files with 151 additions and 50 deletions
|
|
@ -1,15 +1,27 @@
|
||||||
// Auth service for LNbits integration
|
// Auth service for LNbits integration
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
|
import { BaseService } from '@/core/base/BaseService'
|
||||||
import { eventBus } from '@/core/event-bus'
|
import { eventBus } from '@/core/event-bus'
|
||||||
import { lnbitsAPI, type LoginCredentials, type RegisterData } from '@/lib/api/lnbits'
|
import { lnbitsAPI, type LoginCredentials, type RegisterData } from '@/lib/api/lnbits'
|
||||||
|
|
||||||
export class AuthService {
|
export class AuthService extends BaseService {
|
||||||
|
// Service metadata
|
||||||
|
protected readonly metadata = {
|
||||||
|
name: 'AuthService',
|
||||||
|
version: '1.0.0',
|
||||||
|
dependencies: [] // Auth service has no dependencies on other services
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public state
|
||||||
public isAuthenticated = ref(false)
|
public isAuthenticated = ref(false)
|
||||||
public user = ref<any>(null)
|
public user = ref<any>(null)
|
||||||
public isLoading = ref(false)
|
public isLoading = ref(false)
|
||||||
|
|
||||||
async initialize(): Promise<void> {
|
/**
|
||||||
console.log('🔑 Initializing auth service...')
|
* Service-specific initialization (called by BaseService)
|
||||||
|
*/
|
||||||
|
protected async onInitialize(): Promise<void> {
|
||||||
|
this.debug('Initializing auth service...')
|
||||||
|
|
||||||
// Check for existing auth state and fetch user data
|
// Check for existing auth state and fetch user data
|
||||||
await this.checkAuth()
|
await this.checkAuth()
|
||||||
|
|
@ -21,7 +33,7 @@ export class AuthService {
|
||||||
|
|
||||||
async checkAuth(): Promise<boolean> {
|
async checkAuth(): Promise<boolean> {
|
||||||
if (!lnbitsAPI.isAuthenticated()) {
|
if (!lnbitsAPI.isAuthenticated()) {
|
||||||
console.log('🔑 No auth token found - user needs to login')
|
this.debug('No auth token found - user needs to login')
|
||||||
this.isAuthenticated.value = false
|
this.isAuthenticated.value = false
|
||||||
this.user.value = null
|
this.user.value = null
|
||||||
return false
|
return false
|
||||||
|
|
@ -34,12 +46,12 @@ export class AuthService {
|
||||||
this.user.value = userData
|
this.user.value = userData
|
||||||
this.isAuthenticated.value = true
|
this.isAuthenticated.value = true
|
||||||
|
|
||||||
console.log('🔑 User authenticated:', userData.username || userData.id, userData.pubkey?.slice(0, 8))
|
this.debug(`User authenticated: ${userData.username || userData.id} (${userData.pubkey?.slice(0, 8)})`)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('🔑 Authentication check failed:', error)
|
this.handleError(error, 'checkAuth')
|
||||||
this.isAuthenticated.value = false
|
this.isAuthenticated.value = false
|
||||||
this.user.value = null
|
this.user.value = null
|
||||||
// Clear invalid token
|
// Clear invalid token
|
||||||
|
|
@ -63,9 +75,9 @@ export class AuthService {
|
||||||
eventBus.emit('auth:login', { user: userData }, 'auth-service')
|
eventBus.emit('auth:login', { user: userData }, 'auth-service')
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Login failed:', error)
|
const err = this.handleError(error, 'login')
|
||||||
eventBus.emit('auth:login-failed', { error }, 'auth-service')
|
eventBus.emit('auth:login-failed', { error: err }, 'auth-service')
|
||||||
throw error
|
throw err
|
||||||
} finally {
|
} finally {
|
||||||
this.isLoading.value = false
|
this.isLoading.value = false
|
||||||
}
|
}
|
||||||
|
|
@ -84,9 +96,9 @@ export class AuthService {
|
||||||
eventBus.emit('auth:login', { user: userData }, 'auth-service')
|
eventBus.emit('auth:login', { user: userData }, 'auth-service')
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Registration failed:', error)
|
const err = this.handleError(error, 'register')
|
||||||
eventBus.emit('auth:login-failed', { error }, 'auth-service')
|
eventBus.emit('auth:login-failed', { error: err }, 'auth-service')
|
||||||
throw error
|
throw err
|
||||||
} finally {
|
} finally {
|
||||||
this.isLoading.value = false
|
this.isLoading.value = false
|
||||||
}
|
}
|
||||||
|
|
@ -104,6 +116,14 @@ export class AuthService {
|
||||||
// Re-fetch user data from API
|
// Re-fetch user data from API
|
||||||
await this.checkAuth()
|
await this.checkAuth()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleanup when service is disposed
|
||||||
|
*/
|
||||||
|
protected async onDispose(): Promise<void> {
|
||||||
|
this.logout()
|
||||||
|
this.debug('Auth service disposed')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Export singleton instance
|
// Export singleton instance
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,10 @@ export const baseModule: ModulePlugin = {
|
||||||
|
|
||||||
// Initialize core services
|
// Initialize core services
|
||||||
await relayHub.initialize(options?.config?.nostr?.relays || [])
|
await relayHub.initialize(options?.config?.nostr?.relays || [])
|
||||||
await auth.initialize()
|
await auth.initialize({
|
||||||
|
waitForDependencies: false, // Auth has no dependencies
|
||||||
|
maxRetries: 1
|
||||||
|
})
|
||||||
|
|
||||||
console.log('✅ Base module installed successfully')
|
console.log('✅ Base module installed successfully')
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
import { SimplePool, type Filter, type Event, type Relay } from 'nostr-tools'
|
import { SimplePool, type Filter, type Event, type Relay } from 'nostr-tools'
|
||||||
|
import { BaseService } from '@/core/base/BaseService'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
// Simple EventEmitter implementation for browser compatibility
|
// Simple EventEmitter implementation for browser compatibility
|
||||||
class EventEmitter {
|
class EventEmitter {
|
||||||
|
|
@ -57,17 +59,29 @@ export interface RelayStatus {
|
||||||
latency?: number
|
latency?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export class RelayHub extends EventEmitter {
|
export class RelayHub extends BaseService {
|
||||||
|
// Service metadata
|
||||||
|
protected readonly metadata = {
|
||||||
|
name: 'RelayHub',
|
||||||
|
version: '1.0.0',
|
||||||
|
dependencies: [] // RelayHub has no dependencies on other services
|
||||||
|
}
|
||||||
|
|
||||||
|
// EventEmitter functionality
|
||||||
|
private eventEmitter = new EventEmitter()
|
||||||
|
|
||||||
|
// RelayHub specific properties
|
||||||
private pool: SimplePool
|
private pool: SimplePool
|
||||||
private relayConfigs: Map<string, RelayConfig> = new Map()
|
private relayConfigs: Map<string, RelayConfig> = new Map()
|
||||||
private connectedRelays: Map<string, Relay> = new Map()
|
private connectedRelays: Map<string, Relay> = new Map()
|
||||||
private subscriptions: Map<string, any> = new Map()
|
private subscriptions: Map<string, any> = new Map()
|
||||||
public isInitialized = false
|
public isInitializedLegacy = false // Keep for backward compatibility
|
||||||
private reconnectInterval?: number
|
private reconnectInterval?: number
|
||||||
private healthCheckInterval?: number
|
private healthCheckInterval?: number
|
||||||
private mobileVisibilityHandler?: () => void
|
private mobileVisibilityHandler?: () => void
|
||||||
|
|
||||||
// Connection state
|
// Connection state - we need both a reactive ref for components and internal state for business logic
|
||||||
|
public isConnected = ref(false)
|
||||||
private _isConnected = false
|
private _isConnected = false
|
||||||
private _connectionAttempts = 0
|
private _connectionAttempts = 0
|
||||||
private readonly maxReconnectAttempts = 5
|
private readonly maxReconnectAttempts = 5
|
||||||
|
|
@ -80,10 +94,24 @@ export class RelayHub extends EventEmitter {
|
||||||
this.setupMobileVisibilityHandling()
|
this.setupMobileVisibilityHandling()
|
||||||
}
|
}
|
||||||
|
|
||||||
get isConnected(): boolean {
|
// Forward EventEmitter methods
|
||||||
return this._isConnected
|
on(event: string, listener: Function): void {
|
||||||
|
this.eventEmitter.on(event, listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
off(event: string, listener: Function): void {
|
||||||
|
this.eventEmitter.off(event, listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
emit(event: string, ...args: any[]): void {
|
||||||
|
this.eventEmitter.emit(event, ...args)
|
||||||
|
}
|
||||||
|
|
||||||
|
removeAllListeners(event?: string): void {
|
||||||
|
this.eventEmitter.removeAllListeners(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
get connectedRelayCount(): number {
|
get connectedRelayCount(): number {
|
||||||
// Return the actual size of connectedRelays map
|
// Return the actual size of connectedRelays map
|
||||||
return this.connectedRelays.size
|
return this.connectedRelays.size
|
||||||
|
|
@ -123,14 +151,38 @@ export class RelayHub extends EventEmitter {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the relay hub with relay configurations
|
* Initialize the relay hub with relay configurations
|
||||||
|
* This is the public API that maintains backward compatibility
|
||||||
*/
|
*/
|
||||||
async initialize(relayUrls: string[]): Promise<void> {
|
async initialize(relayUrls: string[]): Promise<void>
|
||||||
if (this.isInitialized) {
|
async initialize(options: any): Promise<void>
|
||||||
console.warn('RelayHub already initialized')
|
async initialize(relayUrlsOrOptions: string[] | any): Promise<void> {
|
||||||
return
|
// Handle backward compatibility for relayUrls array
|
||||||
|
if (Array.isArray(relayUrlsOrOptions)) {
|
||||||
|
this.pendingRelayUrls = relayUrlsOrOptions
|
||||||
|
// Use BaseService's initialize method
|
||||||
|
await super.initialize({
|
||||||
|
waitForDependencies: false, // RelayHub has no dependencies
|
||||||
|
maxRetries: 1
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// This is a call from BaseService or other services
|
||||||
|
await super.initialize(relayUrlsOrOptions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private pendingRelayUrls: string[] = []
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service-specific initialization (called by BaseService)
|
||||||
|
*/
|
||||||
|
protected async onInitialize(): Promise<void> {
|
||||||
|
const relayUrls = this.pendingRelayUrls
|
||||||
|
|
||||||
|
if (!relayUrls || relayUrls.length === 0) {
|
||||||
|
throw new Error('No relay URLs provided for initialization')
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('🔧 RelayHub: Initializing with URLs:', relayUrls)
|
this.debug(`Initializing with URLs: ${relayUrls.join(', ')}`)
|
||||||
|
|
||||||
// Convert URLs to relay configs
|
// Convert URLs to relay configs
|
||||||
this.relayConfigs.clear()
|
this.relayConfigs.clear()
|
||||||
|
|
@ -143,14 +195,14 @@ export class RelayHub extends EventEmitter {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log('🔧 RelayHub: Relay configs created:', Array.from(this.relayConfigs.values()))
|
this.debug(`Relay configs created: ${this.relayConfigs.size} configs`)
|
||||||
|
|
||||||
// Start connection management
|
// Start connection management
|
||||||
console.log('🔧 RelayHub: Starting connection...')
|
this.debug('Starting connection...')
|
||||||
await this.connect()
|
await this.connect()
|
||||||
this.startHealthCheck()
|
this.startHealthCheck()
|
||||||
this.isInitialized = true
|
this.isInitializedLegacy = true // Keep for backward compatibility
|
||||||
console.log('🔧 RelayHub: Initialization complete')
|
this.debug('Initialization complete')
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -200,6 +252,7 @@ export class RelayHub extends EventEmitter {
|
||||||
|
|
||||||
if (successfulConnections.length > 0) {
|
if (successfulConnections.length > 0) {
|
||||||
this._isConnected = true
|
this._isConnected = true
|
||||||
|
this.isConnected.value = true
|
||||||
this._connectionAttempts = 0
|
this._connectionAttempts = 0
|
||||||
console.log('🔧 RelayHub: Connection successful, connected to', successfulConnections.length, 'relays')
|
console.log('🔧 RelayHub: Connection successful, connected to', successfulConnections.length, 'relays')
|
||||||
this.emit('connected', successfulConnections.length)
|
this.emit('connected', successfulConnections.length)
|
||||||
|
|
@ -210,6 +263,7 @@ export class RelayHub extends EventEmitter {
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this._isConnected = false
|
this._isConnected = false
|
||||||
|
this.isConnected.value = false
|
||||||
console.error('🔧 RelayHub: Connection failed with error:', error)
|
console.error('🔧 RelayHub: Connection failed with error:', error)
|
||||||
this.emit('connectionError', error)
|
this.emit('connectionError', error)
|
||||||
|
|
||||||
|
|
@ -228,8 +282,6 @@ export class RelayHub extends EventEmitter {
|
||||||
* Disconnect from all relays
|
* Disconnect from all relays
|
||||||
*/
|
*/
|
||||||
disconnect(): void {
|
disconnect(): void {
|
||||||
|
|
||||||
|
|
||||||
// Clear intervals
|
// Clear intervals
|
||||||
if (this.reconnectInterval) {
|
if (this.reconnectInterval) {
|
||||||
clearTimeout(this.reconnectInterval)
|
clearTimeout(this.reconnectInterval)
|
||||||
|
|
@ -250,6 +302,7 @@ export class RelayHub extends EventEmitter {
|
||||||
this.connectedRelays.clear()
|
this.connectedRelays.clear()
|
||||||
|
|
||||||
this._isConnected = false
|
this._isConnected = false
|
||||||
|
this.isConnected.value = false
|
||||||
this.emit('disconnected')
|
this.emit('disconnected')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -448,6 +501,7 @@ export class RelayHub extends EventEmitter {
|
||||||
// Update connection status
|
// Update connection status
|
||||||
if (this.connectedRelays.size === 0) {
|
if (this.connectedRelays.size === 0) {
|
||||||
this._isConnected = false
|
this._isConnected = false
|
||||||
|
this.isConnected.value = false
|
||||||
this.emit('allRelaysDisconnected')
|
this.emit('allRelaysDisconnected')
|
||||||
console.warn('All relays disconnected, attempting reconnection...')
|
console.warn('All relays disconnected, attempting reconnection...')
|
||||||
await this.connect()
|
await this.connect()
|
||||||
|
|
@ -489,6 +543,7 @@ export class RelayHub extends EventEmitter {
|
||||||
window.addEventListener('offline', () => {
|
window.addEventListener('offline', () => {
|
||||||
console.log('Network offline, marking as disconnected...')
|
console.log('Network offline, marking as disconnected...')
|
||||||
this._isConnected = false
|
this._isConnected = false
|
||||||
|
this.isConnected.value = false
|
||||||
this.emit('networkOffline')
|
this.emit('networkOffline')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -498,8 +553,13 @@ export class RelayHub extends EventEmitter {
|
||||||
* Cleanup resources
|
* Cleanup resources
|
||||||
*/
|
*/
|
||||||
destroy(): void {
|
destroy(): void {
|
||||||
|
this.dispose()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleanup when service is disposed (called by BaseService)
|
||||||
|
*/
|
||||||
|
protected async onDispose(): Promise<void> {
|
||||||
// Remove event listeners
|
// Remove event listeners
|
||||||
if (this.mobileVisibilityHandler && typeof document !== 'undefined') {
|
if (this.mobileVisibilityHandler && typeof document !== 'undefined') {
|
||||||
document.removeEventListener('visibilitychange', this.mobileVisibilityHandler)
|
document.removeEventListener('visibilitychange', this.mobileVisibilityHandler)
|
||||||
|
|
@ -513,7 +573,9 @@ export class RelayHub extends EventEmitter {
|
||||||
// Disconnect and cleanup
|
// Disconnect and cleanup
|
||||||
this.disconnect()
|
this.disconnect()
|
||||||
this.removeAllListeners()
|
this.removeAllListeners()
|
||||||
this.isInitialized = false
|
this.isInitializedLegacy = false
|
||||||
|
|
||||||
|
this.debug('RelayHub disposed')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,7 @@ import MarketSettings from './components/MarketSettings.vue'
|
||||||
import MerchantStore from './components/MerchantStore.vue'
|
import MerchantStore from './components/MerchantStore.vue'
|
||||||
import ShoppingCart from './components/ShoppingCart.vue'
|
import ShoppingCart from './components/ShoppingCart.vue'
|
||||||
|
|
||||||
// Import services
|
// NostrmarketService will be dynamically imported in install()
|
||||||
import { NostrmarketService } from './services/nostrmarketService'
|
|
||||||
|
|
||||||
// Store will be imported when needed
|
// Store will be imported when needed
|
||||||
|
|
||||||
|
|
@ -46,8 +45,19 @@ export const marketModule: ModulePlugin = {
|
||||||
throw new Error('Market module requires configuration')
|
throw new Error('Market module requires configuration')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create and register services
|
// Import the singleton instance
|
||||||
const nostrmarketService = new NostrmarketService()
|
const { nostrmarketService } = await import('./services/nostrmarketService')
|
||||||
|
|
||||||
|
// Initialize the service (will handle dependency injection)
|
||||||
|
await nostrmarketService.initialize({
|
||||||
|
waitForDependencies: true,
|
||||||
|
maxRetries: 3
|
||||||
|
}).catch(error => {
|
||||||
|
console.warn('🛒 NostrmarketService initialization deferred:', error)
|
||||||
|
// Service will auto-initialize when dependencies are available
|
||||||
|
})
|
||||||
|
|
||||||
|
// Register the service
|
||||||
container.provide(NOSTRMARKET_SERVICE_TOKEN, nostrmarketService)
|
container.provide(NOSTRMARKET_SERVICE_TOKEN, nostrmarketService)
|
||||||
|
|
||||||
// Register global components
|
// Register global components
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { finalizeEvent, type EventTemplate, nip04 } from 'nostr-tools'
|
import { finalizeEvent, type EventTemplate, nip04 } from 'nostr-tools'
|
||||||
import { injectService, SERVICE_TOKENS } from '@/core/di-container'
|
import { BaseService } from '@/core/base/BaseService'
|
||||||
import { auth } from '@/composables/useAuth'
|
|
||||||
import type { Stall, Product, Order } from '@/stores/market'
|
import type { Stall, Product, Order } from '@/stores/market'
|
||||||
|
|
||||||
export interface NostrmarketStall {
|
export interface NostrmarketStall {
|
||||||
|
|
@ -67,13 +66,20 @@ export interface NostrmarketOrderStatus {
|
||||||
shipped?: boolean
|
shipped?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export class NostrmarketService {
|
export class NostrmarketService extends BaseService {
|
||||||
private get relayHub(): any {
|
// Service metadata
|
||||||
const hub = injectService(SERVICE_TOKENS.RELAY_HUB)
|
protected readonly metadata = {
|
||||||
if (!hub) {
|
name: 'NostrmarketService',
|
||||||
throw new Error('RelayHub not available. Make sure base module is installed.')
|
version: '1.0.0',
|
||||||
}
|
dependencies: ['RelayHub', 'AuthService']
|
||||||
return hub as any
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service-specific initialization (called by BaseService)
|
||||||
|
*/
|
||||||
|
protected async onInitialize(): Promise<void> {
|
||||||
|
this.debug('NostrmarketService initialized')
|
||||||
|
// Service doesn't need special initialization
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -100,12 +106,12 @@ export class NostrmarketService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private getAuth() {
|
private getAuth() {
|
||||||
if (!auth.isAuthenticated.value || !auth.currentUser.value?.prvkey) {
|
if (!this.authService?.isAuthenticated?.value || !this.authService?.user?.value?.prvkey) {
|
||||||
throw new Error('User not authenticated or private key not available')
|
throw new Error('User not authenticated or private key not available')
|
||||||
}
|
}
|
||||||
|
|
||||||
const pubkey = auth.currentUser.value.pubkey
|
const pubkey = this.authService.user.value.pubkey
|
||||||
const prvkey = auth.currentUser.value.prvkey
|
const prvkey = this.authService.user.value.prvkey
|
||||||
|
|
||||||
if (!pubkey || !prvkey) {
|
if (!pubkey || !prvkey) {
|
||||||
throw new Error('Public key or private key is missing')
|
throw new Error('Public key or private key is missing')
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue