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:
parent
093846b351
commit
4a3d2012be
6 changed files with 69 additions and 18 deletions
|
|
@ -48,6 +48,7 @@ export abstract class BaseService {
|
||||||
protected storageService: any = null
|
protected storageService: any = null
|
||||||
protected toastService: any = null
|
protected toastService: any = null
|
||||||
protected invoiceService: any = null
|
protected invoiceService: any = null
|
||||||
|
protected lnbitsAPI: any = null
|
||||||
|
|
||||||
// Service state
|
// Service state
|
||||||
public readonly isInitialized: Ref<boolean> = ref(false)
|
public readonly isInitialized: Ref<boolean> = ref(false)
|
||||||
|
|
@ -140,6 +141,7 @@ export abstract class BaseService {
|
||||||
this.storageService = tryInjectService(SERVICE_TOKENS.STORAGE_SERVICE)
|
this.storageService = tryInjectService(SERVICE_TOKENS.STORAGE_SERVICE)
|
||||||
this.toastService = tryInjectService(SERVICE_TOKENS.TOAST_SERVICE)
|
this.toastService = tryInjectService(SERVICE_TOKENS.TOAST_SERVICE)
|
||||||
this.invoiceService = tryInjectService(SERVICE_TOKENS.INVOICE_SERVICE)
|
this.invoiceService = tryInjectService(SERVICE_TOKENS.INVOICE_SERVICE)
|
||||||
|
this.lnbitsAPI = tryInjectService(SERVICE_TOKENS.LNBITS_API)
|
||||||
|
|
||||||
// Check if all required dependencies are available
|
// Check if all required dependencies are available
|
||||||
const missingDeps = this.getMissingDependencies()
|
const missingDeps = this.getMissingDependencies()
|
||||||
|
|
@ -193,6 +195,9 @@ export abstract class BaseService {
|
||||||
if (deps.includes('ToastService') && !this.toastService) {
|
if (deps.includes('ToastService') && !this.toastService) {
|
||||||
missing.push('ToastService')
|
missing.push('ToastService')
|
||||||
}
|
}
|
||||||
|
if (deps.includes('LnbitsAPI') && !this.lnbitsAPI) {
|
||||||
|
missing.push('LnbitsAPI')
|
||||||
|
}
|
||||||
|
|
||||||
return missing
|
return missing
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -140,6 +140,9 @@ export const SERVICE_TOKENS = {
|
||||||
|
|
||||||
// Nostrmarket services
|
// Nostrmarket services
|
||||||
NOSTRMARKET_SERVICE: Symbol('nostrmarketService'),
|
NOSTRMARKET_SERVICE: Symbol('nostrmarketService'),
|
||||||
|
|
||||||
|
// API services
|
||||||
|
LNBITS_API: Symbol('lnbitsAPI'),
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
// Type-safe injection helpers
|
// Type-safe injection helpers
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import type { Event, Ticket } from '../types/event'
|
import type { Event, Ticket } from '../types/event'
|
||||||
import { config } from '@/lib/config'
|
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_BASE_URL = config.api.baseUrl || 'http://lnbits'
|
||||||
const API_KEY = config.api.key
|
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 }> {
|
export async function purchaseTicket(eventId: string): Promise<{ payment_hash: string; payment_request: string }> {
|
||||||
try {
|
try {
|
||||||
|
// Get injected LnbitsAPI service
|
||||||
|
const lnbitsAPI = injectService(SERVICE_TOKENS.LNBITS_API) as LnbitsAPI
|
||||||
|
|
||||||
// Get current user to ensure authentication
|
// Get current user to ensure authentication
|
||||||
const user = await lnbitsAPI.getCurrentUser()
|
const user = await lnbitsAPI.getCurrentUser()
|
||||||
if (!user) {
|
if (!user) {
|
||||||
|
|
@ -135,6 +139,9 @@ export async function checkPaymentStatus(eventId: string, paymentHash: string):
|
||||||
|
|
||||||
export async function fetchUserTickets(userId: string): Promise<Ticket[]> {
|
export async function fetchUserTickets(userId: string): Promise<Ticket[]> {
|
||||||
try {
|
try {
|
||||||
|
// Get injected LnbitsAPI service
|
||||||
|
const lnbitsAPI = injectService(SERVICE_TOKENS.LNBITS_API) as LnbitsAPI
|
||||||
|
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`${API_BASE_URL}/events/api/v1/tickets/user/${userId}`,
|
`${API_BASE_URL}/events/api/v1/tickets/user/${userId}`,
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -61,16 +61,33 @@ interface User {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import { BaseService } from '@/core/base/BaseService'
|
||||||
import { getApiUrl, getAuthToken, setAuthToken, removeAuthToken } from '@/lib/config/lnbits'
|
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
|
private accessToken: string | null = null
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
super()
|
||||||
// Try to load token from localStorage
|
// Try to load token from localStorage
|
||||||
this.accessToken = getAuthToken()
|
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>(
|
private async request<T>(
|
||||||
endpoint: string,
|
endpoint: string,
|
||||||
options: RequestInit = {}
|
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 }
|
export type { LoginCredentials, RegisterData, AuthResponse, User }
|
||||||
|
|
@ -2,14 +2,14 @@
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
import { BaseService } from '@/core/base/BaseService'
|
import { BaseService } from '@/core/base/BaseService'
|
||||||
import { eventBus } from '@/core/event-bus'
|
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 {
|
export class AuthService extends BaseService {
|
||||||
// Service metadata
|
// Service metadata
|
||||||
protected readonly metadata = {
|
protected readonly metadata = {
|
||||||
name: 'AuthService',
|
name: 'AuthService',
|
||||||
version: '1.0.0',
|
version: '1.0.0',
|
||||||
dependencies: [] // Auth service has no dependencies on other services
|
dependencies: ['LnbitsAPI'] // Auth service depends on LnbitsAPI for authentication
|
||||||
}
|
}
|
||||||
|
|
||||||
// Public state
|
// Public state
|
||||||
|
|
@ -47,7 +47,7 @@ export class AuthService extends BaseService {
|
||||||
}
|
}
|
||||||
|
|
||||||
async checkAuth(): Promise<boolean> {
|
async checkAuth(): Promise<boolean> {
|
||||||
if (!lnbitsAPI.isAuthenticated()) {
|
if (!this.lnbitsAPI.isAuthenticated()) {
|
||||||
this.debug('No auth token found - user needs to login')
|
this.debug('No auth token found - user needs to login')
|
||||||
this.isAuthenticated.value = false
|
this.isAuthenticated.value = false
|
||||||
this.user.value = null
|
this.user.value = null
|
||||||
|
|
@ -56,7 +56,7 @@ export class AuthService extends BaseService {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.isLoading.value = true
|
this.isLoading.value = true
|
||||||
const userData = await lnbitsAPI.getCurrentUser()
|
const userData = await this.lnbitsAPI.getCurrentUser()
|
||||||
|
|
||||||
this.user.value = userData
|
this.user.value = userData
|
||||||
this.isAuthenticated.value = true
|
this.isAuthenticated.value = true
|
||||||
|
|
@ -70,7 +70,7 @@ export class AuthService extends BaseService {
|
||||||
this.isAuthenticated.value = false
|
this.isAuthenticated.value = false
|
||||||
this.user.value = null
|
this.user.value = null
|
||||||
// Clear invalid token
|
// Clear invalid token
|
||||||
lnbitsAPI.logout()
|
this.lnbitsAPI.logout()
|
||||||
return false
|
return false
|
||||||
} finally {
|
} finally {
|
||||||
this.isLoading.value = false
|
this.isLoading.value = false
|
||||||
|
|
@ -81,8 +81,8 @@ export class AuthService extends BaseService {
|
||||||
this.isLoading.value = true
|
this.isLoading.value = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await lnbitsAPI.login(credentials)
|
await this.lnbitsAPI.login(credentials)
|
||||||
const userData = await lnbitsAPI.getCurrentUser()
|
const userData = await this.lnbitsAPI.getCurrentUser()
|
||||||
|
|
||||||
this.user.value = userData
|
this.user.value = userData
|
||||||
this.isAuthenticated.value = true
|
this.isAuthenticated.value = true
|
||||||
|
|
@ -102,8 +102,8 @@ export class AuthService extends BaseService {
|
||||||
this.isLoading.value = true
|
this.isLoading.value = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await lnbitsAPI.register(data)
|
await this.lnbitsAPI.register(data)
|
||||||
const userData = await lnbitsAPI.getCurrentUser()
|
const userData = await this.lnbitsAPI.getCurrentUser()
|
||||||
|
|
||||||
this.user.value = userData
|
this.user.value = userData
|
||||||
this.isAuthenticated.value = true
|
this.isAuthenticated.value = true
|
||||||
|
|
@ -120,7 +120,7 @@ export class AuthService extends BaseService {
|
||||||
}
|
}
|
||||||
|
|
||||||
async logout(): Promise<void> {
|
async logout(): Promise<void> {
|
||||||
lnbitsAPI.logout()
|
this.lnbitsAPI.logout()
|
||||||
this.user.value = null
|
this.user.value = null
|
||||||
this.isAuthenticated.value = false
|
this.isAuthenticated.value = false
|
||||||
this.error.value = null
|
this.error.value = null
|
||||||
|
|
@ -134,14 +134,14 @@ export class AuthService extends BaseService {
|
||||||
}
|
}
|
||||||
|
|
||||||
async initialize(): Promise<void> {
|
async initialize(): Promise<void> {
|
||||||
// Alias for checkAuth for compatibility
|
// Call BaseService initialize first to inject dependencies
|
||||||
await this.checkAuth()
|
await super.initialize()
|
||||||
}
|
}
|
||||||
|
|
||||||
async updatePassword(currentPassword: string, newPassword: string): Promise<void> {
|
async updatePassword(currentPassword: string, newPassword: string): Promise<void> {
|
||||||
try {
|
try {
|
||||||
this.isLoading.value = true
|
this.isLoading.value = true
|
||||||
const updatedUser = await lnbitsAPI.updatePassword(currentPassword, newPassword)
|
const updatedUser = await this.lnbitsAPI.updatePassword(currentPassword, newPassword)
|
||||||
this.user.value = updatedUser
|
this.user.value = updatedUser
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const err = this.handleError(error, 'updatePassword')
|
const err = this.handleError(error, 'updatePassword')
|
||||||
|
|
@ -154,7 +154,7 @@ export class AuthService extends BaseService {
|
||||||
async updateProfile(data: Partial<User>): Promise<void> {
|
async updateProfile(data: Partial<User>): Promise<void> {
|
||||||
try {
|
try {
|
||||||
this.isLoading.value = true
|
this.isLoading.value = true
|
||||||
const updatedUser = await lnbitsAPI.updateProfile(data)
|
const updatedUser = await this.lnbitsAPI.updateProfile(data)
|
||||||
this.user.value = updatedUser
|
this.user.value = updatedUser
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const err = this.handleError(error, 'updateProfile')
|
const err = this.handleError(error, 'updateProfile')
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,11 @@ import { visibilityService } from '@/core/services/VisibilityService'
|
||||||
import { storageService } from '@/core/services/StorageService'
|
import { storageService } from '@/core/services/StorageService'
|
||||||
import { toastService } from '@/core/services/ToastService'
|
import { toastService } from '@/core/services/ToastService'
|
||||||
import { InvoiceService } from '@/core/services/invoiceService'
|
import { InvoiceService } from '@/core/services/invoiceService'
|
||||||
|
import { LnbitsAPI } from '@/lib/api/lnbits'
|
||||||
|
|
||||||
// Create service instances
|
// Create service instances
|
||||||
const invoiceService = new InvoiceService()
|
const invoiceService = new InvoiceService()
|
||||||
|
const lnbitsAPI = new LnbitsAPI()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base Module Plugin
|
* Base Module Plugin
|
||||||
|
|
@ -51,12 +53,22 @@ export const baseModule: ModulePlugin = {
|
||||||
// Register invoice service
|
// Register invoice service
|
||||||
container.provide(SERVICE_TOKENS.INVOICE_SERVICE, invoiceService)
|
container.provide(SERVICE_TOKENS.INVOICE_SERVICE, invoiceService)
|
||||||
|
|
||||||
|
// Register API services
|
||||||
|
container.provide(SERVICE_TOKENS.LNBITS_API, lnbitsAPI)
|
||||||
|
|
||||||
// Register PWA service
|
// Register PWA service
|
||||||
container.provide('pwaService', pwaService)
|
container.provide('pwaService', pwaService)
|
||||||
|
|
||||||
// Initialize core services
|
// Initialize core services in dependency order
|
||||||
relayHub.setRelayUrls(options?.config?.nostr?.relays || [])
|
relayHub.setRelayUrls(options?.config?.nostr?.relays || [])
|
||||||
await relayHub.initialize()
|
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
|
// Auth initialization moved to app.ts before router guards
|
||||||
await paymentService.initialize({
|
await paymentService.initialize({
|
||||||
waitForDependencies: true, // PaymentService depends on AuthService
|
waitForDependencies: true, // PaymentService depends on AuthService
|
||||||
|
|
@ -74,6 +86,7 @@ export const baseModule: ModulePlugin = {
|
||||||
waitForDependencies: false, // ToastService has no dependencies
|
waitForDependencies: false, // ToastService has no dependencies
|
||||||
maxRetries: 1
|
maxRetries: 1
|
||||||
})
|
})
|
||||||
|
// InvoiceService doesn't need initialization as it's not a BaseService
|
||||||
|
|
||||||
console.log('✅ Base module installed successfully')
|
console.log('✅ Base module installed successfully')
|
||||||
},
|
},
|
||||||
|
|
@ -88,6 +101,12 @@ export const baseModule: ModulePlugin = {
|
||||||
await visibilityService.dispose()
|
await visibilityService.dispose()
|
||||||
await storageService.dispose()
|
await storageService.dispose()
|
||||||
await toastService.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')
|
console.log('✅ Base module uninstalled')
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue