Complete LnbitsAPI migration to dependency injection pattern

- Convert LnbitsAPI from singleton to BaseService extension
- Add LNBITS_API service token to DI container
- Register LnbitsAPI service in base module with proper initialization order
- Update AuthService to depend on injected LnbitsAPI instead of singleton
- Fix BaseService to properly track LnbitsAPI dependency in getMissingDependencies
- Update events API functions to use dependency injection
- Resolve initialization timing issue preventing application startup

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
padreug 2025-09-07 01:58:36 +02:00
parent 093846b351
commit 4a3d2012be
6 changed files with 69 additions and 18 deletions

View file

@ -48,6 +48,7 @@ export abstract class BaseService {
protected storageService: any = null
protected toastService: any = null
protected invoiceService: any = null
protected lnbitsAPI: any = null
// Service state
public readonly isInitialized: Ref<boolean> = ref(false)
@ -140,6 +141,7 @@ export abstract class BaseService {
this.storageService = tryInjectService(SERVICE_TOKENS.STORAGE_SERVICE)
this.toastService = tryInjectService(SERVICE_TOKENS.TOAST_SERVICE)
this.invoiceService = tryInjectService(SERVICE_TOKENS.INVOICE_SERVICE)
this.lnbitsAPI = tryInjectService(SERVICE_TOKENS.LNBITS_API)
// Check if all required dependencies are available
const missingDeps = this.getMissingDependencies()
@ -193,6 +195,9 @@ export abstract class BaseService {
if (deps.includes('ToastService') && !this.toastService) {
missing.push('ToastService')
}
if (deps.includes('LnbitsAPI') && !this.lnbitsAPI) {
missing.push('LnbitsAPI')
}
return missing
}

View file

@ -140,6 +140,9 @@ export const SERVICE_TOKENS = {
// Nostrmarket services
NOSTRMARKET_SERVICE: Symbol('nostrmarketService'),
// API services
LNBITS_API: Symbol('lnbitsAPI'),
} as const
// Type-safe injection helpers

View file

@ -1,6 +1,7 @@
import type { Event, Ticket } from '../types/event'
import { config } from '@/lib/config'
import { lnbitsAPI } from './lnbits'
import { injectService, SERVICE_TOKENS } from '@/core/di-container'
import type { LnbitsAPI } from './lnbits'
const API_BASE_URL = config.api.baseUrl || 'http://lnbits'
const API_KEY = config.api.key
@ -39,6 +40,9 @@ export async function fetchEvents(): Promise<Event[]> {
export async function purchaseTicket(eventId: string): Promise<{ payment_hash: string; payment_request: string }> {
try {
// Get injected LnbitsAPI service
const lnbitsAPI = injectService(SERVICE_TOKENS.LNBITS_API) as LnbitsAPI
// Get current user to ensure authentication
const user = await lnbitsAPI.getCurrentUser()
if (!user) {
@ -135,6 +139,9 @@ export async function checkPaymentStatus(eventId: string, paymentHash: string):
export async function fetchUserTickets(userId: string): Promise<Ticket[]> {
try {
// Get injected LnbitsAPI service
const lnbitsAPI = injectService(SERVICE_TOKENS.LNBITS_API) as LnbitsAPI
const response = await fetch(
`${API_BASE_URL}/events/api/v1/tickets/user/${userId}`,
{

View file

@ -61,16 +61,33 @@ interface User {
}
}
import { BaseService } from '@/core/base/BaseService'
import { getApiUrl, getAuthToken, setAuthToken, removeAuthToken } from '@/lib/config/lnbits'
class LnbitsAPI {
export class LnbitsAPI extends BaseService {
// Service metadata
protected readonly metadata = {
name: 'LnbitsAPI',
version: '1.0.0',
dependencies: [] // No dependencies - this is a core infrastructure service
}
private accessToken: string | null = null
constructor() {
super()
// Try to load token from localStorage
this.accessToken = getAuthToken()
}
/**
* Service-specific initialization (called by BaseService)
*/
protected async onInitialize(): Promise<void> {
this.debug('LnbitsAPI initialized')
// Service is ready to use
}
private async request<T>(
endpoint: string,
options: RequestInit = {}
@ -183,5 +200,5 @@ class LnbitsAPI {
}
}
export const lnbitsAPI = new LnbitsAPI()
// Service is now registered in the DI container
export type { LoginCredentials, RegisterData, AuthResponse, User }

View file

@ -2,14 +2,14 @@
import { ref, computed } from 'vue'
import { BaseService } from '@/core/base/BaseService'
import { eventBus } from '@/core/event-bus'
import { lnbitsAPI, type LoginCredentials, type RegisterData, type User } from '@/lib/api/lnbits'
import type { LoginCredentials, RegisterData, User } from '@/lib/api/lnbits'
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
dependencies: ['LnbitsAPI'] // Auth service depends on LnbitsAPI for authentication
}
// Public state
@ -47,7 +47,7 @@ export class AuthService extends BaseService {
}
async checkAuth(): Promise<boolean> {
if (!lnbitsAPI.isAuthenticated()) {
if (!this.lnbitsAPI.isAuthenticated()) {
this.debug('No auth token found - user needs to login')
this.isAuthenticated.value = false
this.user.value = null
@ -56,7 +56,7 @@ export class AuthService extends BaseService {
try {
this.isLoading.value = true
const userData = await lnbitsAPI.getCurrentUser()
const userData = await this.lnbitsAPI.getCurrentUser()
this.user.value = userData
this.isAuthenticated.value = true
@ -70,7 +70,7 @@ export class AuthService extends BaseService {
this.isAuthenticated.value = false
this.user.value = null
// Clear invalid token
lnbitsAPI.logout()
this.lnbitsAPI.logout()
return false
} finally {
this.isLoading.value = false
@ -81,8 +81,8 @@ export class AuthService extends BaseService {
this.isLoading.value = true
try {
await lnbitsAPI.login(credentials)
const userData = await lnbitsAPI.getCurrentUser()
await this.lnbitsAPI.login(credentials)
const userData = await this.lnbitsAPI.getCurrentUser()
this.user.value = userData
this.isAuthenticated.value = true
@ -102,8 +102,8 @@ export class AuthService extends BaseService {
this.isLoading.value = true
try {
await lnbitsAPI.register(data)
const userData = await lnbitsAPI.getCurrentUser()
await this.lnbitsAPI.register(data)
const userData = await this.lnbitsAPI.getCurrentUser()
this.user.value = userData
this.isAuthenticated.value = true
@ -120,7 +120,7 @@ export class AuthService extends BaseService {
}
async logout(): Promise<void> {
lnbitsAPI.logout()
this.lnbitsAPI.logout()
this.user.value = null
this.isAuthenticated.value = false
this.error.value = null
@ -134,14 +134,14 @@ export class AuthService extends BaseService {
}
async initialize(): Promise<void> {
// Alias for checkAuth for compatibility
await this.checkAuth()
// Call BaseService initialize first to inject dependencies
await super.initialize()
}
async updatePassword(currentPassword: string, newPassword: string): Promise<void> {
try {
this.isLoading.value = true
const updatedUser = await lnbitsAPI.updatePassword(currentPassword, newPassword)
const updatedUser = await this.lnbitsAPI.updatePassword(currentPassword, newPassword)
this.user.value = updatedUser
} catch (error) {
const err = this.handleError(error, 'updatePassword')
@ -154,7 +154,7 @@ export class AuthService extends BaseService {
async updateProfile(data: Partial<User>): Promise<void> {
try {
this.isLoading.value = true
const updatedUser = await lnbitsAPI.updateProfile(data)
const updatedUser = await this.lnbitsAPI.updateProfile(data)
this.user.value = updatedUser
} catch (error) {
const err = this.handleError(error, 'updateProfile')

View file

@ -15,9 +15,11 @@ import { visibilityService } from '@/core/services/VisibilityService'
import { storageService } from '@/core/services/StorageService'
import { toastService } from '@/core/services/ToastService'
import { InvoiceService } from '@/core/services/invoiceService'
import { LnbitsAPI } from '@/lib/api/lnbits'
// Create service instances
const invoiceService = new InvoiceService()
const lnbitsAPI = new LnbitsAPI()
/**
* Base Module Plugin
@ -51,12 +53,22 @@ export const baseModule: ModulePlugin = {
// Register invoice service
container.provide(SERVICE_TOKENS.INVOICE_SERVICE, invoiceService)
// Register API services
container.provide(SERVICE_TOKENS.LNBITS_API, lnbitsAPI)
// Register PWA service
container.provide('pwaService', pwaService)
// Initialize core services
// Initialize core services in dependency order
relayHub.setRelayUrls(options?.config?.nostr?.relays || [])
await relayHub.initialize()
// Initialize LnbitsAPI first since AuthService depends on it
await lnbitsAPI.initialize({
waitForDependencies: false, // LnbitsAPI is core infrastructure with no dependencies
maxRetries: 1
})
// Auth initialization moved to app.ts before router guards
await paymentService.initialize({
waitForDependencies: true, // PaymentService depends on AuthService
@ -74,6 +86,7 @@ export const baseModule: ModulePlugin = {
waitForDependencies: false, // ToastService has no dependencies
maxRetries: 1
})
// InvoiceService doesn't need initialization as it's not a BaseService
console.log('✅ Base module installed successfully')
},
@ -88,6 +101,12 @@ export const baseModule: ModulePlugin = {
await visibilityService.dispose()
await storageService.dispose()
await toastService.dispose()
// InvoiceService doesn't need disposal as it's not a BaseService
await lnbitsAPI.dispose()
// Remove services from DI container
container.remove(SERVICE_TOKENS.LNBITS_API)
container.remove(SERVICE_TOKENS.INVOICE_SERVICE)
console.log('✅ Base module uninstalled')
},