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
import { ref } from 'vue'
import { eventBus } from '@/core/event-bus'
import { getAuthToken } from '@/lib/config/lnbits'
import { config } from '@/lib/config'
import { lnbitsAPI, type LoginCredentials, type RegisterData } from '@/lib/api/lnbits'
export class AuthService {
public isAuthenticated = ref(false)
@ -21,39 +20,16 @@ export class AuthService {
}
async checkAuth(): Promise<boolean> {
const authToken = getAuthToken()
if (!authToken) {
if (!lnbitsAPI.isAuthenticated()) {
console.log('🔑 No auth token found - user needs to login')
this.isAuthenticated.value = false
this.user.value = null
return false
}
// Fetch current user data from API
try {
this.isLoading.value = true
const API_BASE_URL = config.api.baseUrl || 'http://localhost:5006'
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()
const userData = await lnbitsAPI.getCurrentUser()
this.user.value = userData
this.isAuthenticated.value = true
@ -66,25 +42,25 @@ export class AuthService {
console.warn('🔑 Authentication check failed:', error)
this.isAuthenticated.value = false
this.user.value = null
// Clear invalid token
lnbitsAPI.logout()
return false
} finally {
this.isLoading.value = false
}
}
async login(credentials: any): Promise<void> {
async login(credentials: LoginCredentials): Promise<void> {
this.isLoading.value = true
try {
// Implement your login logic here
// For demo purposes, we'll accept any credentials
this.user.value = credentials
await lnbitsAPI.login(credentials)
const userData = await lnbitsAPI.getCurrentUser()
this.user.value = userData
this.isAuthenticated.value = true
// Store auth state
localStorage.setItem('auth', JSON.stringify(credentials))
eventBus.emit('auth:login', { user: credentials }, 'auth-service')
eventBus.emit('auth:login', { user: userData }, 'auth-service')
} catch (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 {
lnbitsAPI.logout()
this.user.value = null
this.isAuthenticated.value = false
localStorage.removeItem('auth')
eventBus.emit('auth:logout', {}, 'auth-service')
}

View file

@ -404,16 +404,29 @@ export class ChatService {
console.log('Loading message history for', peerPubkeys.length, '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],
authors: [userPubkey, ...peerPubkeys], // Messages from us or peers
'#p': [userPubkey], // Messages tagged with our pubkey
limit: 100 // Limit to last 100 messages per conversation
authors: peerPubkeys, // Messages from peers
'#p': [userPubkey], // Messages tagged to us
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
for (const event of events) {

View file

@ -70,8 +70,31 @@ export function useMarket() {
// Load market data from Nostr events
const loadMarketData = async (marketData: any) => {
try {
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
const events = await relayHub.queryEvents([
{
@ -80,6 +103,8 @@ export function useMarket() {
'#d': [marketData.identifier]
}
])
console.log('🛒 Found', events.length, 'market events')
// Process market events
@ -90,7 +115,7 @@ export function useMarket() {
const market = {
d: marketData.identifier,
pubkey: marketData.pubkey,
relays: config.market.supportedRelays,
relays: config.nostr.relays,
selected: true,
opts: JSON.parse(marketEvent.content)
}
@ -103,7 +128,7 @@ export function useMarket() {
const market = {
d: marketData.identifier,
pubkey: marketData.pubkey,
relays: config.market.supportedRelays,
relays: config.nostr.relays,
selected: true,
opts: {
name: 'Ariège Market',
@ -122,7 +147,7 @@ export function useMarket() {
const market = {
d: marketData.identifier,
pubkey: marketData.pubkey,
relays: config.market.supportedRelays,
relays: config.nostr.relays,
selected: true,
opts: {
name: 'Default Market',
@ -159,6 +184,8 @@ export function useMarket() {
authors: merchants
}
])
console.log('🛒 Found', events.length, 'stall events for', merchants.length, 'merchants')
// Process stall events
@ -221,6 +248,8 @@ export function useMarket() {
authors: merchants
}
])
console.log('🛒 Found', events.length, 'product events for', merchants.length, 'merchants')
// Process product events
@ -463,35 +492,46 @@ export function useMarket() {
// Connect to market
const connectToMarket = async () => {
try {
console.log('🛒 Checking RelayHub connection...')
// Use existing relay hub connection (should already be connected by base module)
isConnected.value = relayHub.isConnected.value
console.log('🛒 RelayHub connected:', isConnected.value)
if (!isConnected.value) {
console.warn('RelayHub not connected, attempting to connect...')
await relayHub.connect()
isConnected.value = relayHub.isConnected.value
if (!isConnected.value) {
throw new Error('Failed to connect to Nostr relays')
}
console.warn('🛒 RelayHub not connected - this is expected if authentication is not complete')
// Don't try to connect here - let the base module handle connections
// Just proceed with offline/demo mode
console.log('🛒 Proceeding in offline mode')
}
// Market connected successfully
console.log('🛒 Market connected successfully')
// Load market data
console.log('🛒 Loading basic market data...')
await loadMarketData({
identifier: 'default',
pubkey: nostrStore.account?.pubkey || ''
})
// Load stalls and products
await loadStalls()
await loadProducts()
// Load stalls and products only if connected
if (isConnected.value) {
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
console.log('🛒 Subscribing to market updates...')
subscribeToMarketUpdates()
} catch (err) {
console.error('🛒 Failed to connect to market:', err)
error.value = err instanceof Error ? err : new Error('Failed to connect to market')
throw err
}

View file

@ -14,32 +14,97 @@ export function useMarketPreloader() {
const preloadMarket = async () => {
// Don't preload if already done or currently preloading
if (isPreloaded.value || isPreloading.value) {
console.log('🛒 Market preload skipped - already loaded or in progress')
return
}
console.log('🛒 Starting market preload...')
try {
isPreloading.value = true
preloadError.value = null
const naddr = config.market.defaultNaddr
if (!naddr) {
console.warn('🛒 No market naddr configured, skipping preload')
return
}
// Connect to market
await market.connectToMarket()
console.log('🛒 Using market naddr:', naddr.slice(0, 20) + '...')
// Load market data
await market.loadMarket(naddr)
// 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
console.log('🛒 Connecting to market...')
await connectWithTimeout()
// Load market data
console.log('🛒 Loading market data...')
await loadWithTimeout()
// Clear any error state since preloading succeeded
marketStore.setError(null)
isPreloaded.value = true
console.log('🛒 Market preload completed successfully')
} catch (error) {
preloadError.value = error instanceof Error ? error.message : 'Failed to preload market'
// Don't throw error, let the UI handle it gracefully
console.error('🛒 Market preload failed:', error)
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 {
isPreloading.value = false
}

View file

@ -184,4 +184,4 @@ onUnmounted(() => {
}
market.disconnectFromMarket()
})
</script>
</script>