Refactor AuthService and market components for improved functionality and error handling

- Integrate LNbits API for authentication in AuthService, replacing token management with direct API calls for user data.
- Enhance login and registration processes to utilize the new API, improving user experience and error handling.
- Update market components to include detailed logging and fallback mechanisms for offline scenarios, ensuring better resilience during market data loading.
- Refactor market preloader to handle connection timeouts and provide sample data as a fallback, enhancing user experience in offline mode.
This commit is contained in:
padreug 2025-09-05 03:07:55 +02:00
parent daa9656680
commit 55e99e002d
5 changed files with 178 additions and 63 deletions

View file

@ -1,8 +1,7 @@
// Auth service for LNbits integration // Auth service for LNbits integration
import { ref } from 'vue' import { ref } from 'vue'
import { eventBus } from '@/core/event-bus' import { eventBus } from '@/core/event-bus'
import { getAuthToken } from '@/lib/config/lnbits' import { lnbitsAPI, type LoginCredentials, type RegisterData } from '@/lib/api/lnbits'
import { config } from '@/lib/config'
export class AuthService { export class AuthService {
public isAuthenticated = ref(false) public isAuthenticated = ref(false)
@ -21,39 +20,16 @@ export class AuthService {
} }
async checkAuth(): Promise<boolean> { async checkAuth(): Promise<boolean> {
const authToken = getAuthToken() if (!lnbitsAPI.isAuthenticated()) {
if (!authToken) {
console.log('🔑 No auth token found - user needs to login') console.log('🔑 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
} }
// Fetch current user data from API
try { try {
this.isLoading.value = true this.isLoading.value = true
const API_BASE_URL = config.api.baseUrl || 'http://localhost:5006' const userData = await lnbitsAPI.getCurrentUser()
const response = await fetch(`${API_BASE_URL}/api/v1/auth/nostr/me`, {
headers: {
'Authorization': `Bearer ${authToken}`,
'Content-Type': 'application/json'
}
})
if (!response.ok) {
if (response.status === 401) {
console.log('🔑 Auth token invalid - user needs to login')
this.logout()
return false
}
console.warn(`🔑 Failed to fetch user data: ${response.status} - authentication may not be properly configured`)
this.isAuthenticated.value = false
this.user.value = null
return false
}
const userData = await response.json()
this.user.value = userData this.user.value = userData
this.isAuthenticated.value = true this.isAuthenticated.value = true
@ -66,25 +42,25 @@ export class AuthService {
console.warn('🔑 Authentication check failed:', error) console.warn('🔑 Authentication check failed:', error)
this.isAuthenticated.value = false this.isAuthenticated.value = false
this.user.value = null this.user.value = null
// Clear invalid token
lnbitsAPI.logout()
return false return false
} finally { } finally {
this.isLoading.value = false this.isLoading.value = false
} }
} }
async login(credentials: any): Promise<void> { async login(credentials: LoginCredentials): Promise<void> {
this.isLoading.value = true this.isLoading.value = true
try { try {
// Implement your login logic here await lnbitsAPI.login(credentials)
// For demo purposes, we'll accept any credentials const userData = await lnbitsAPI.getCurrentUser()
this.user.value = credentials
this.user.value = userData
this.isAuthenticated.value = true this.isAuthenticated.value = true
// Store auth state eventBus.emit('auth:login', { user: userData }, 'auth-service')
localStorage.setItem('auth', JSON.stringify(credentials))
eventBus.emit('auth:login', { user: credentials }, 'auth-service')
} catch (error) { } catch (error) {
console.error('Login failed:', error) console.error('Login failed:', error)
@ -95,10 +71,31 @@ export class AuthService {
} }
} }
async register(data: RegisterData): Promise<void> {
this.isLoading.value = true
try {
await lnbitsAPI.register(data)
const userData = await lnbitsAPI.getCurrentUser()
this.user.value = userData
this.isAuthenticated.value = true
eventBus.emit('auth:login', { user: userData }, 'auth-service')
} catch (error) {
console.error('Registration failed:', error)
eventBus.emit('auth:login-failed', { error }, 'auth-service')
throw error
} finally {
this.isLoading.value = false
}
}
logout(): void { logout(): void {
lnbitsAPI.logout()
this.user.value = null this.user.value = null
this.isAuthenticated.value = false this.isAuthenticated.value = false
localStorage.removeItem('auth')
eventBus.emit('auth:logout', {}, 'auth-service') eventBus.emit('auth:logout', {}, 'auth-service')
} }

View file

@ -404,16 +404,29 @@ export class ChatService {
console.log('Loading message history for', peerPubkeys.length, 'peers') console.log('Loading message history for', peerPubkeys.length, 'peers')
// Query historical messages (kind 4) to/from known peers // Query historical messages (kind 4) to/from known peers
const events = await relayHub.queryEvents([ // We need separate queries for sent vs received messages due to different tagging
const receivedEvents = await relayHub.queryEvents([
{ {
kinds: [4], kinds: [4],
authors: [userPubkey, ...peerPubkeys], // Messages from us or peers authors: peerPubkeys, // Messages from peers
'#p': [userPubkey], // Messages tagged with our pubkey '#p': [userPubkey], // Messages tagged to us
limit: 100 // Limit to last 100 messages per conversation limit: 100
} }
]) ])
console.log('Found', events.length, 'historical messages') const sentEvents = await relayHub.queryEvents([
{
kinds: [4],
authors: [userPubkey], // Messages from us
'#p': peerPubkeys, // Messages tagged to peers
limit: 100
}
])
const events = [...receivedEvents, ...sentEvents]
.sort((a, b) => a.created_at - b.created_at) // Sort by timestamp
console.log('Found', events.length, 'historical messages:', receivedEvents.length, 'received,', sentEvents.length, 'sent')
// Process historical messages // Process historical messages
for (const event of events) { for (const event of events) {

View file

@ -70,8 +70,31 @@ export function useMarket() {
// Load market data from Nostr events // Load market data from Nostr events
const loadMarketData = async (marketData: any) => { const loadMarketData = async (marketData: any) => {
try { try {
// Load market data from Nostr events console.log('🛒 Loading market data for:', { identifier: marketData.identifier, pubkey: marketData.pubkey?.slice(0, 8) })
// Check if we can query events (relays are connected)
if (!isConnected.value) {
console.log('🛒 Not connected to relays, creating default market')
// Create default market without trying to fetch from Nostr
const market = {
d: marketData.identifier,
pubkey: marketData.pubkey,
relays: config.nostr.relays,
selected: true,
opts: {
name: 'Demo Market (Offline)',
description: 'Demo market running in offline mode',
merchants: [],
ui: {}
}
}
marketStore.addMarket(market)
marketStore.setActiveMarket(market)
return
}
// Load market data from Nostr events
// Fetch market configuration event // Fetch market configuration event
const events = await relayHub.queryEvents([ const events = await relayHub.queryEvents([
{ {
@ -81,6 +104,8 @@ export function useMarket() {
} }
]) ])
console.log('🛒 Found', events.length, 'market events')
// Process market events // Process market events
if (events.length > 0) { if (events.length > 0) {
@ -90,7 +115,7 @@ export function useMarket() {
const market = { const market = {
d: marketData.identifier, d: marketData.identifier,
pubkey: marketData.pubkey, pubkey: marketData.pubkey,
relays: config.market.supportedRelays, relays: config.nostr.relays,
selected: true, selected: true,
opts: JSON.parse(marketEvent.content) opts: JSON.parse(marketEvent.content)
} }
@ -103,7 +128,7 @@ export function useMarket() {
const market = { const market = {
d: marketData.identifier, d: marketData.identifier,
pubkey: marketData.pubkey, pubkey: marketData.pubkey,
relays: config.market.supportedRelays, relays: config.nostr.relays,
selected: true, selected: true,
opts: { opts: {
name: 'Ariège Market', name: 'Ariège Market',
@ -122,7 +147,7 @@ export function useMarket() {
const market = { const market = {
d: marketData.identifier, d: marketData.identifier,
pubkey: marketData.pubkey, pubkey: marketData.pubkey,
relays: config.market.supportedRelays, relays: config.nostr.relays,
selected: true, selected: true,
opts: { opts: {
name: 'Default Market', name: 'Default Market',
@ -160,6 +185,8 @@ export function useMarket() {
} }
]) ])
console.log('🛒 Found', events.length, 'stall events for', merchants.length, 'merchants')
// Process stall events // Process stall events
// Group events by stall ID and keep only the most recent version // Group events by stall ID and keep only the most recent version
@ -222,6 +249,8 @@ export function useMarket() {
} }
]) ])
console.log('🛒 Found', events.length, 'product events for', merchants.length, 'merchants')
// Process product events // Process product events
// Group events by product ID and keep only the most recent version // Group events by product ID and keep only the most recent version
@ -463,35 +492,46 @@ export function useMarket() {
// Connect to market // Connect to market
const connectToMarket = async () => { const connectToMarket = async () => {
try { try {
console.log('🛒 Checking RelayHub connection...')
// Use existing relay hub connection (should already be connected by base module) // Use existing relay hub connection (should already be connected by base module)
isConnected.value = relayHub.isConnected.value isConnected.value = relayHub.isConnected.value
console.log('🛒 RelayHub connected:', isConnected.value)
if (!isConnected.value) { if (!isConnected.value) {
console.warn('RelayHub not connected, attempting to connect...') console.warn('🛒 RelayHub not connected - this is expected if authentication is not complete')
await relayHub.connect() // Don't try to connect here - let the base module handle connections
isConnected.value = relayHub.isConnected.value // Just proceed with offline/demo mode
console.log('🛒 Proceeding in offline mode')
if (!isConnected.value) {
throw new Error('Failed to connect to Nostr relays')
}
} }
// Market connected successfully console.log('🛒 Market connected successfully')
// Load market data // Load market data
console.log('🛒 Loading basic market data...')
await loadMarketData({ await loadMarketData({
identifier: 'default', identifier: 'default',
pubkey: nostrStore.account?.pubkey || '' pubkey: nostrStore.account?.pubkey || ''
}) })
// Load stalls and products // Load stalls and products only if connected
await loadStalls() if (isConnected.value) {
await loadProducts() console.log('🛒 Loading stalls...')
await loadStalls()
console.log('🛒 Loading products...')
await loadProducts()
} else {
console.log('🛒 Skipping stalls/products loading - not connected to relays')
// Add sample products for demo mode
console.log('🛒 Adding sample products for offline demo...')
addSampleProducts()
}
// Subscribe to updates // Subscribe to updates
console.log('🛒 Subscribing to market updates...')
subscribeToMarketUpdates() subscribeToMarketUpdates()
} catch (err) { } catch (err) {
console.error('🛒 Failed to connect to market:', err)
error.value = err instanceof Error ? err : new Error('Failed to connect to market') error.value = err instanceof Error ? err : new Error('Failed to connect to market')
throw err throw err
} }

View file

@ -14,32 +14,97 @@ export function useMarketPreloader() {
const preloadMarket = async () => { const preloadMarket = async () => {
// Don't preload if already done or currently preloading // Don't preload if already done or currently preloading
if (isPreloaded.value || isPreloading.value) { if (isPreloaded.value || isPreloading.value) {
console.log('🛒 Market preload skipped - already loaded or in progress')
return return
} }
console.log('🛒 Starting market preload...')
try { try {
isPreloading.value = true isPreloading.value = true
preloadError.value = null preloadError.value = null
const naddr = config.market.defaultNaddr const naddr = config.market.defaultNaddr
if (!naddr) { if (!naddr) {
console.warn('🛒 No market naddr configured, skipping preload')
return return
} }
console.log('🛒 Using market naddr:', naddr.slice(0, 20) + '...')
// Add timeout protection to prevent hanging
const timeoutMs = 30000 // 30 seconds
const connectWithTimeout = async () => {
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => reject(new Error('Market connection timeout after 30 seconds')), timeoutMs)
})
return Promise.race([
market.connectToMarket(),
timeoutPromise
])
}
const loadWithTimeout = async () => {
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => reject(new Error('Market loading timeout after 30 seconds')), timeoutMs)
})
return Promise.race([
market.loadMarket(naddr),
timeoutPromise
])
}
// Connect to market // Connect to market
await market.connectToMarket() console.log('🛒 Connecting to market...')
await connectWithTimeout()
// Load market data // Load market data
await market.loadMarket(naddr) console.log('🛒 Loading market data...')
await loadWithTimeout()
// Clear any error state since preloading succeeded // Clear any error state since preloading succeeded
marketStore.setError(null) marketStore.setError(null)
isPreloaded.value = true isPreloaded.value = true
console.log('🛒 Market preload completed successfully')
} catch (error) { } catch (error) {
preloadError.value = error instanceof Error ? error.message : 'Failed to preload market' console.error('🛒 Market preload failed:', error)
// Don't throw error, let the UI handle it gracefully console.log('🛒 Attempting fallback with sample data...')
try {
// Create a basic market with sample data as fallback
const fallbackMarket = {
d: 'demo-market',
pubkey: 'demo',
relays: ['ws://localhost:5006/nostrrelay/test1'],
selected: true,
opts: {
name: 'Demo Market',
description: 'Demo market with sample products',
merchants: [],
ui: {}
}
}
marketStore.addMarket(fallbackMarket)
marketStore.setActiveMarket(fallbackMarket)
// Add some sample products if the useMarket composable supports it
if (market.addSampleProducts && typeof market.addSampleProducts === 'function') {
market.addSampleProducts()
}
isPreloaded.value = true
console.log('🛒 Market preload completed with fallback data')
} catch (fallbackError) {
console.error('🛒 Fallback also failed:', fallbackError)
preloadError.value = error instanceof Error ? error.message : 'Failed to preload market'
}
} finally { } finally {
isPreloading.value = false isPreloading.value = false
} }