Enhance Chat and Market Services with improved error handling and logging

Chat Service:
- Added detailed logging for API calls and responses, including warnings for missing authentication tokens and invalid response formats.
- Implemented a retry mechanism for message subscription setup on connection errors.
- Merged peers instead of clearing existing ones when loading from the API.

Market Service:
- Updated authentication checks to prioritize global auth state, improving user experience during order placement.
- Enhanced error messages for missing Nostr keys to guide users in configuring their profiles.

These changes improve the robustness and user-friendliness of the chat and market functionalities.
This commit is contained in:
padreug 2025-09-06 20:12:41 +02:00
parent 034f3ce80f
commit 8a019db34a
3 changed files with 92 additions and 29 deletions

View file

@ -316,10 +316,13 @@ export class ChatService extends BaseService {
try { try {
const authToken = getAuthToken() const authToken = getAuthToken()
if (!authToken) { if (!authToken) {
console.warn('💬 No authentication token found for loading peers from API')
throw new Error('No authentication token found') throw new Error('No authentication token found')
} }
const API_BASE_URL = config.api.baseUrl || 'http://localhost:5006' const API_BASE_URL = config.api.baseUrl || 'http://localhost:5006'
console.log('💬 Loading peers from API:', `${API_BASE_URL}/api/v1/auth/nostr/pubkeys`)
const response = await fetch(`${API_BASE_URL}/api/v1/auth/nostr/pubkeys`, { const response = await fetch(`${API_BASE_URL}/api/v1/auth/nostr/pubkeys`, {
headers: { headers: {
'Authorization': `Bearer ${authToken}`, 'Authorization': `Bearer ${authToken}`,
@ -328,15 +331,26 @@ export class ChatService extends BaseService {
}) })
if (!response.ok) { if (!response.ok) {
throw new Error(`Failed to load peers: ${response.status}`) const errorText = await response.text()
console.error('💬 API response error:', response.status, errorText)
throw new Error(`Failed to load peers: ${response.status} - ${errorText}`)
} }
const data = await response.json() const data = await response.json()
console.log('💬 API returned', data?.length || 0, 'peers')
// Clear existing peers and load from API if (!Array.isArray(data)) {
this.peers.value.clear() console.warn('💬 Invalid API response format - expected array, got:', typeof data)
return
}
// Don't clear existing peers - merge instead
data.forEach((peer: any) => { data.forEach((peer: any) => {
if (!peer.pubkey) {
console.warn('💬 Skipping peer without pubkey:', peer)
return
}
const chatPeer: ChatPeer = { const chatPeer: ChatPeer = {
pubkey: peer.pubkey, pubkey: peer.pubkey,
name: peer.username || `User ${peer.pubkey.slice(0, 8)}`, name: peer.username || `User ${peer.pubkey.slice(0, 8)}`,
@ -349,25 +363,31 @@ export class ChatService extends BaseService {
// Save to storage // Save to storage
this.savePeersToStorage() this.savePeersToStorage()
console.log(`Loaded ${data.length} peers from API`) console.log(`Loaded ${data.length} peers from API, total peers now: ${this.peers.value.size}`)
} catch (error) { } catch (error) {
console.error('Failed to load peers from API:', error) console.error('Failed to load peers from API:', error)
throw error // Don't re-throw - peers from storage are still available
} }
} }
private loadPeersFromStorage(): void { private loadPeersFromStorage(): void {
// Skip loading peers in constructor as StorageService may not be available yet // Skip loading peers in constructor as StorageService may not be available yet
// This will be called later during initialization when dependencies are ready // This will be called later during initialization when dependencies are ready
if (!this.isInitialized.value) { if (!this.isInitialized.value || !this.storageService) {
this.debug('Skipping peer loading from storage - not initialized or storage unavailable')
return return
} }
const peersArray = this.storageService.getUserData('chat-peers', []) as ChatPeer[] try {
peersArray.forEach(peer => { const peersArray = this.storageService.getUserData('chat-peers', []) as ChatPeer[]
this.peers.value.set(peer.pubkey, peer) console.log('💬 Loading', peersArray.length, 'peers from storage')
}) peersArray.forEach(peer => {
this.peers.value.set(peer.pubkey, peer)
})
} catch (error) {
console.warn('💬 Failed to load peers from storage:', error)
}
} }
private savePeersToStorage(): void { private savePeersToStorage(): void {
@ -465,22 +485,36 @@ export class ChatService extends BaseService {
if (!this.relayHub || !this.authService?.user?.value?.pubkey) { if (!this.relayHub || !this.authService?.user?.value?.pubkey) {
console.warn('💬 Cannot setup message subscription: missing services') console.warn('💬 Cannot setup message subscription: missing services')
// Retry after a short delay
setTimeout(() => this.setupMessageSubscription(), 2000)
return return
} }
if (!this.relayHub.isConnected) { if (!this.relayHub.isConnected.value) {
console.warn('💬 RelayHub not connected, waiting for connection...') console.warn('💬 RelayHub not connected, waiting for connection...')
// Listen for connection event // Listen for connection event - but also set up retry
this.relayHub.on('connected', () => { const connectHandler = () => {
console.log('💬 RelayHub connected, setting up message subscription...') console.log('💬 RelayHub connected, setting up message subscription...')
this.relayHub.off('connected', connectHandler) // Remove listener to prevent multiple calls
this.setupMessageSubscription() this.setupMessageSubscription()
}) }
this.relayHub.on('connected', connectHandler)
// Also set up a fallback retry mechanism
setTimeout(() => {
if (!this.subscriptionUnsubscriber && this.relayHub.isConnected.value) {
console.log('💬 Retry mechanism triggered - setting up subscription')
this.setupMessageSubscription()
}
}, 5000)
return return
} }
const userPubkey = this.authService.user.value.pubkey const userPubkey = this.authService.user.value.pubkey
// Subscribe to encrypted direct messages (kind 4) addressed to this user // Subscribe to encrypted direct messages (kind 4) addressed to this user
console.log('💬 Setting up chat message subscription for user:', userPubkey.slice(0, 8))
this.subscriptionUnsubscriber = this.relayHub.subscribe({ this.subscriptionUnsubscriber = this.relayHub.subscribe({
id: 'chat-messages', id: 'chat-messages',
filters: [ filters: [
@ -495,17 +529,25 @@ export class ChatService extends BaseService {
return return
} }
console.log('💬 Received encrypted message from:', event.pubkey.slice(0, 8))
await this.processIncomingMessage(event) await this.processIncomingMessage(event)
}, },
onEose: () => { onEose: () => {
console.log('Chat message subscription EOSE received') console.log('💬 Chat message subscription EOSE received - subscription active')
} }
}) })
console.log('Chat message subscription set up successfully') console.log('Chat message subscription set up successfully')
} catch (error) { } catch (error) {
console.error('Failed to setup message subscription:', error) console.error('❌ Failed to setup message subscription:', error)
// Retry after a delay on error
setTimeout(() => {
if (!this.subscriptionUnsubscriber) {
console.log('💬 Retrying message subscription setup after error...')
this.setupMessageSubscription()
}
}, 3000)
} }
} }

View file

@ -1,6 +1,7 @@
import { finalizeEvent, type EventTemplate, nip04 } from 'nostr-tools' import { finalizeEvent, type EventTemplate, nip04 } from 'nostr-tools'
import { BaseService } from '@/core/base/BaseService' import { BaseService } from '@/core/base/BaseService'
import type { Stall, Product, Order } from '@/stores/market' import type { Stall, Product, Order } from '@/stores/market'
import { auth } from '@/composables/useAuth'
export interface NostrmarketStall { export interface NostrmarketStall {
id: string id: string
@ -106,15 +107,32 @@ export class NostrmarketService extends BaseService {
} }
private getAuth() { private getAuth() {
if (!this.authService?.isAuthenticated?.value || !this.authService?.user?.value?.prvkey) { // Check global auth first
throw new Error('User not authenticated or private key not available') if (!auth.isAuthenticated.value) {
throw new Error('User not authenticated')
} }
// Try to get keys from global auth first, fallback to injected auth service
const globalUser = auth.currentUser.value
const serviceUser = this.authService?.user?.value
const pubkey = this.authService.user.value.pubkey const pubkey = globalUser?.pubkey || serviceUser?.pubkey
const prvkey = this.authService.user.value.prvkey const prvkey = globalUser?.prvkey || serviceUser?.prvkey
if (!pubkey || !prvkey) { if (!pubkey || !prvkey) {
throw new Error('Public key or private key is missing') this.debug('Auth check failed:', {
globalUser: {
exists: !!globalUser,
hasPubkey: !!globalUser?.pubkey,
hasPrvkey: !!globalUser?.prvkey
},
serviceUser: {
exists: !!serviceUser,
hasPubkey: !!serviceUser?.pubkey,
hasPrvkey: !!serviceUser?.prvkey
}
})
throw new Error('Nostr keys not available. Please ensure your Nostr identity is configured in your profile.')
} }
// Validate that we have proper hex strings // Validate that we have proper hex strings

View file

@ -272,6 +272,7 @@ import { ref, computed, onMounted } from 'vue'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import { useMarketStore } from '@/stores/market' import { useMarketStore } from '@/stores/market'
import { injectService, SERVICE_TOKENS } from '@/core/di-container' import { injectService, SERVICE_TOKENS } from '@/core/di-container'
import { auth } from '@/composables/useAuth'
import { import {
Card, Card,
CardHeader, CardHeader,
@ -411,21 +412,23 @@ const placeOrder = async () => {
} }
// Debug logging to understand auth state // Debug logging to understand auth state
console.log('Auth check:', { console.log('Auth check:', {
isAuthenticated: authService.isAuthenticated.value, isAuthenticated: auth.isAuthenticated.value,
user: authService.user.value, user: auth.currentUser.value,
userPubkey: auth.currentUser.value?.pubkey,
authServiceUser: authService.user.value,
hasPubkey: !!authService.user.value?.pubkey, hasPubkey: !!authService.user.value?.pubkey,
nostrPubkey: authService.user.value?.pubkey nostrPubkey: authService.user.value?.pubkey
}) })
// Get pubkey from auth service // Try to get pubkey from main auth first, fallback to auth service
const userPubkey = authService.user.value?.pubkey const userPubkey = auth.currentUser.value?.pubkey || authService.user.value?.pubkey
if (!authService.isAuthenticated.value) { if (!auth.isAuthenticated.value) {
throw new Error('You must be logged in to place an order') throw new Error('You must be logged in to place an order')
} }
if (!userPubkey) { if (!userPubkey) {
throw new Error('No Nostr public key found. Please ensure your Nostr identity is configured.') throw new Error('Nostr identity required: Please configure your Nostr public key in your profile settings to place orders.')
} }
// Create the order using the market store's order placement functionality // Create the order using the market store's order placement functionality