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:
parent
daa9656680
commit
55e99e002d
5 changed files with 178 additions and 63 deletions
|
|
@ -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')
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
if (isConnected.value) {
|
||||||
|
console.log('🛒 Loading stalls...')
|
||||||
await loadStalls()
|
await loadStalls()
|
||||||
|
console.log('🛒 Loading products...')
|
||||||
await loadProducts()
|
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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
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'
|
preloadError.value = error instanceof Error ? error.message : 'Failed to preload market'
|
||||||
// Don't throw error, let the UI handle it gracefully
|
}
|
||||||
} finally {
|
} finally {
|
||||||
isPreloading.value = false
|
isPreloading.value = false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue