web-app/docs/03-core-services/index.md
padreug 71cec00bfc Add Wallet Module documentation and WebSocket integration
- Introduced comprehensive documentation for the new Wallet Module, detailing its purpose, features, and key components.
- Added WebSocket integration documentation, outlining real-time balance updates, connection management, and error handling.
- Updated README and module index files to include references to the Wallet Module, enhancing overall module visibility and accessibility.

These changes improve the clarity and usability of the Wallet Module, providing developers with essential information for implementation and integration.
2025-09-18 09:56:19 +02:00

603 lines
No EOL
20 KiB
Markdown

# ⚙️ Core Services Overview
> **Shared infrastructure services** providing foundational functionality across all modules with reactive architecture, dependency injection, and lifecycle management.
## Table of Contents
- [[#Service Architecture]]
- [[#Available Services]]
- [[#Service Lifecycle]]
- [[#Dependency Injection]]
- [[#Service Development]]
- [[#Testing Services]]
## Service Architecture
### **BaseService Foundation**
All core services extend the `BaseService` abstract class which provides:
- **Reactive State Management** - Integration with Vue's reactivity system
- **Lifecycle Management** - Standardized initialization and disposal
- **Error Handling** - Consistent error patterns across services
- **Type Safety** - Full TypeScript support with strict typing
```typescript
abstract class BaseService {
protected isInitialized = ref(false)
protected isDisposed = ref(false)
abstract initialize(): Promise<void>
abstract dispose(): Promise<void>
// Reactive state helpers
protected createReactiveState<T>(initialValue: T): Ref<T>
protected createComputedState<T>(getter: () => T): ComputedRef<T>
}
```
### **Service Registration Pattern**
Services are registered in the dependency injection container during module installation:
```typescript
// Service registration (in base module)
container.provide(SERVICE_TOKENS.RELAY_HUB, relayHub)
container.provide(SERVICE_TOKENS.AUTH_SERVICE, authService)
container.provide(SERVICE_TOKENS.STORAGE_SERVICE, storageService)
// Service consumption (anywhere in app)
const relayHub = injectService(SERVICE_TOKENS.RELAY_HUB)
const auth = injectService(SERVICE_TOKENS.AUTH_SERVICE)
```
## Available Services
### **AuthService** 🔐
**Purpose:** User authentication and identity management
**Location:** `src/modules/base/auth/auth-service.ts`
**Token:** `SERVICE_TOKENS.AUTH_SERVICE`
**Key Features:**
- **Key Management** - Secure generation, import, and storage of Nostr keys
- **User Sessions** - Persistent authentication with encrypted storage
- **Profile Management** - User profile creation and updates
- **Security** - Client-side key handling with no server storage
**Reactive State:**
```typescript
interface AuthService {
user: Ref<NostrUser | null> // Current authenticated user
isAuthenticated: ComputedRef<boolean> // Authentication status
isLoading: Ref<boolean> // Loading state
loginError: Ref<string | null> // Login error message
}
```
**Key Methods:**
- `generateKeyPair()` - Create new Nostr key pair
- `loginWithPrivateKey(privateKey: string)` - Authenticate with existing key
- `logout()` - Clear session and user data
- `updateProfile(profile: UserMetadata)` - Update user profile
**See:** [[authentication|📖 Authentication Service Documentation]]
### **PaymentService** 💳
**Purpose:** Centralized Lightning payment processing and wallet management
**Location:** `src/core/services/PaymentService.ts`
**Token:** `SERVICE_TOKENS.PAYMENT_SERVICE`
**Key Features:**
- **Wallet Management** - Multi-wallet support with preferred wallet selection
- **Payment Processing** - Lightning invoices, QR code generation, and external wallet handling
- **Balance Management** - Real-time wallet balance updates and tracking
- **Payment Validation** - Balance checking and payment amount validation
- **Error Handling** - Consistent payment error handling and user feedback
**Reactive State:**
```typescript
interface PaymentService {
isProcessingPayment: ComputedRef<boolean> // Payment in progress
paymentError: ComputedRef<string | null> // Payment error state
totalBalance: number // Total balance across wallets (getter)
hasWalletWithBalance: boolean // Wallet availability check (getter)
}
```
**Key Methods:**
- `payWithWallet(paymentRequest, amount?, options?)` - Process Lightning payment
- `generateQRCode(paymentRequest, options?)` - Generate payment QR codes
- `openExternalWallet(paymentRequest)` - Launch external Lightning wallet
- `updateWalletBalance(balanceMsat, walletId?)` - Update wallet balance from WebSocket
- `getPreferredWallet()` - Get the primary wallet for operations
- `getWalletWithBalance(requiredAmount?)` - Find wallet with sufficient balance
**Wallet Selection Logic:**
- Always uses first wallet (`wallets[0]`) for consistency across modules
- Provides helper methods for wallet selection and balance checking
- Integrates with AuthService for wallet credentials and management
**See:** [[payment-service|📖 Payment Service Documentation]]
### **RelayHub** 🌐
**Purpose:** Centralized Nostr relay connection management
**Location:** `src/modules/base/nostr/relay-hub.ts`
**Token:** `SERVICE_TOKENS.RELAY_HUB`
**Key Features:**
- **Connection Management** - Automatic connection, reconnection, and failover
- **Event Publishing** - Reliable event publishing across multiple relays
- **Subscription Management** - Efficient event subscriptions with deduplication
- **Performance Monitoring** - Relay latency and success rate tracking
**Reactive State:**
```typescript
interface RelayHub {
connectedRelays: Ref<string[]> // Currently connected relays
connectionStatus: ComputedRef<ConnectionStatus> // Overall connection status
relayStats: Ref<Map<string, RelayStats>> // Per-relay statistics
isConnecting: Ref<boolean> // Connection in progress
}
```
**Key Methods:**
- `connect(relays: string[])` - Connect to relay URLs
- `publishEvent(event: NostrEvent)` - Publish event to all connected relays
- `subscribe(filters: Filter[], callback: EventCallback)` - Subscribe to events
- `getRelayInfo(url: string)` - Get relay connection information
**See:** [[../01-architecture/relay-hub|📖 Relay Hub Architecture Documentation]]
### **StorageService** 💾
**Purpose:** User-scoped local storage operations
**Location:** `src/core/services/StorageService.ts`
**Token:** `SERVICE_TOKENS.STORAGE_SERVICE`
**Key Features:**
- **User-Scoped Storage** - Automatic key prefixing per authenticated user
- **Type-Safe Operations** - Strongly typed get/set operations with JSON serialization
- **Reactive Updates** - Optional reactive storage with Vue refs
- **Migration Support** - Data migration between storage schema versions
**Key Methods:**
```typescript
interface StorageService {
setUserData<T>(key: string, data: T): void
getUserData<T>(key: string, defaultValue?: T): T | undefined
removeUserData(key: string): void
clearUserData(): void
// Reactive variants
getReactiveUserData<T>(key: string, defaultValue: T): Ref<T>
setReactiveUserData<T>(key: string, ref: Ref<T>): void
}
```
**Storage Patterns:**
- User-specific keys: `user:{pubkey}:settings`
- Global application keys: `app:theme`
- Module-specific keys: `user:{pubkey}:chat:contacts`
**See:** [[storage-service|📖 Storage Service Documentation]]
### **ToastService** 📢
**Purpose:** Application-wide notifications and user feedback
**Location:** `src/core/services/ToastService.ts`
**Token:** `SERVICE_TOKENS.TOAST_SERVICE`
**Key Features:**
- **Context-Specific Methods** - Pre-configured toasts for common scenarios
- **Consistent Messaging** - Standardized success, error, and info messages
- **Accessibility** - Screen reader compatible notifications
- **Customizable** - Support for custom toast content and actions
**Organized by Context:**
```typescript
interface ToastService {
// Authentication context
auth: {
loginSuccess(): void
loginError(error?: string): void
logoutSuccess(): void
keyGenerated(): void
}
// Payment context
payment: {
invoiceCreated(): void
paymentReceived(): void
paymentFailed(error?: string): void
}
// Clipboard operations
clipboard: {
copied(item?: string): void
copyFailed(): void
}
// General operations
operation: {
success(message: string): void
error(message: string): void
info(message: string): void
}
}
```
**See:** [[toast-service|📖 Toast Service Documentation]]
### **EventBus** 📡
**Purpose:** Inter-module communication and event coordination
**Location:** `src/core/services/EventBus.ts`
**Token:** `SERVICE_TOKENS.EVENT_BUS`
**Key Features:**
- **Type-Safe Events** - Strongly typed event payloads
- **Module Isolation** - Clean communication between modules
- **Event Namespacing** - Organized event names by domain
- **Subscription Management** - Easy subscribe/unsubscribe patterns
**Event Categories:**
```typescript
interface EventBusEvents {
// User events
'user:authenticated': { userId: string, profile: UserMetadata }
'user:profile-updated': { userId: string, changes: Partial<UserMetadata> }
'user:logout': { userId: string }
// Chat events
'chat:message-received': { messageId: string, from: string, content: string }
'chat:typing-start': { from: string, chatId: string }
// Payment events
'payment:invoice-created': { invoiceId: string, amount: number }
'payment:received': { invoiceId: string, amount: number }
// Relay events
'relay:connected': { url: string }
'relay:disconnected': { url: string, reason?: string }
}
```
**See:** [[../01-architecture/event-bus|📖 Event Bus Communication Documentation]]
### **WalletService** 🏦
**Purpose:** Wallet operations and transaction management
**Location:** `src/modules/wallet/services/WalletService.ts`
**Token:** `SERVICE_TOKENS.WALLET_SERVICE`
**Key Features:**
- **Payment Operations** - Send Lightning payments to invoices, addresses, and LNURL
- **Payment Link Management** - Create and manage LNURL payment links and Lightning addresses
- **Transaction History** - Load and manage comprehensive transaction history
- **Real-time Updates** - Add new transactions from WebSocket notifications
**Reactive State:**
```typescript
interface WalletService {
transactions: ComputedRef<PaymentTransaction[]> // Transaction history
payLinks: ComputedRef<PayLink[]> // Created payment links
isCreatingPayLink: ComputedRef<boolean> // Pay link creation state
isSendingPayment: ComputedRef<boolean> // Payment sending state
error: ComputedRef<string | null> // Error state
}
```
**Key Methods:**
- `sendPayment(request: SendPaymentRequest)` - Send Lightning payment
- `createReceiveAddress(params)` - Create LNURL payment link with Lightning address
- `deletePayLink(linkId: string)` - Remove payment link
- `addTransaction(payment)` - Add transaction from WebSocket notification
- `refresh()` - Reload transactions and payment links
### **WalletWebSocketService** ⚡
**Purpose:** Real-time wallet balance updates via WebSocket
**Location:** `src/modules/wallet/services/WalletWebSocketService.ts`
**Token:** `SERVICE_TOKENS.WALLET_WEBSOCKET_SERVICE`
**Key Features:**
- **Real-time Connection** - WebSocket connection to LNbits for instant updates
- **Balance Synchronization** - Smart balance updates with unit conversion handling
- **Payment Notifications** - Toast notifications for incoming payments
- **Connection Management** - Automatic reconnection with exponential backoff
- **Battery Optimization** - VisibilityService integration for mobile efficiency
**Reactive State:**
```typescript
interface WalletWebSocketService {
isConnected: Ref<boolean> // WebSocket connection status
connectionStatus: Ref<string> // Connection state details
}
```
**Connection States:**
- `disconnected` - Not connected
- `connecting` - Connection in progress
- `connected` - Successfully connected
- `reconnecting` - Attempting to reconnect
- `error` - Connection failed
- `failed` - Max reconnection attempts reached
**Key Methods:**
- `reconnect()` - Manual reconnection trigger
- `disconnect()` - Graceful disconnection
- `cleanup()` - Service disposal
**WebSocket Integration:**
- Connects to `wss://lnbits-server/api/v1/ws/{walletInkey}`
- Handles incoming/outgoing payment balance differences
- Updates PaymentService with converted balance (sats → millisats)
- Integrates with WalletService for transaction updates
## Service Lifecycle
### **Initialization Phase**
Services are initialized in dependency order during application startup:
```typescript
// 1. Base services (no dependencies)
await authService.initialize()
await storageService.initialize()
// 2. Infrastructure services (depend on base services)
await relayHub.initialize()
await toastService.initialize()
// 3. Feature services (depend on infrastructure)
await chatService.initialize()
await eventsService.initialize()
```
### **Service Dependencies**
Services declare their dependencies through constructor injection:
```typescript
class ChatService extends BaseService {
constructor(
private auth = injectService(SERVICE_TOKENS.AUTH_SERVICE),
private relayHub = injectService(SERVICE_TOKENS.RELAY_HUB),
private storage = injectService(SERVICE_TOKENS.STORAGE_SERVICE),
private eventBus = injectService(SERVICE_TOKENS.EVENT_BUS)
) {
super()
}
}
```
### **Disposal Phase**
Services are disposed in reverse dependency order during application shutdown:
```typescript
async dispose(): Promise<void> {
// Clean up subscriptions
this.subscriptions.forEach(sub => sub.close())
// Save persistent state
await this.storage.setUserData('chat:messages', this.messages.value)
// Mark as disposed
this.isDisposed.value = true
}
```
## Dependency Injection
### **Service Tokens**
Type-safe service tokens prevent runtime errors and enable proper TypeScript inference:
```typescript
export const SERVICE_TOKENS = {
AUTH_SERVICE: Symbol('AUTH_SERVICE') as InjectionKey<AuthService>,
RELAY_HUB: Symbol('RELAY_HUB') as InjectionKey<RelayHub>,
STORAGE_SERVICE: Symbol('STORAGE_SERVICE') as InjectionKey<StorageService>,
TOAST_SERVICE: Symbol('TOAST_SERVICE') as InjectionKey<ToastService>,
} as const
```
### **Service Registration**
Services are registered during module installation:
```typescript
// In base module installation
export async function installBaseModule(app: App) {
// Create service instances
const authService = new AuthService()
const relayHub = new RelayHub()
const storageService = new StorageService()
// Register in container
container.provide(SERVICE_TOKENS.AUTH_SERVICE, authService)
container.provide(SERVICE_TOKENS.RELAY_HUB, relayHub)
container.provide(SERVICE_TOKENS.STORAGE_SERVICE, storageService)
// Initialize services
await authService.initialize()
await relayHub.initialize()
}
```
### **Service Consumption**
Services are injected where needed using type-safe injection:
```typescript
// In composables
export function useAuth() {
const authService = injectService(SERVICE_TOKENS.AUTH_SERVICE)
return {
user: authService.user,
login: authService.login,
logout: authService.logout
}
}
// In components
<script setup>
const toast = injectService(SERVICE_TOKENS.TOAST_SERVICE)
const handleSuccess = () => toast.operation.success('Action completed!')
</script>
```
## Service Development
### **Creating a New Service**
#### 1. Service Class Implementation
```typescript
// src/core/services/MyService.ts
export class MyService extends BaseService {
// Reactive state
private readonly _data = ref<MyData[]>([])
private readonly _isLoading = ref(false)
// Public readonly access to state
public readonly data = readonly(this._data)
public readonly isLoading = readonly(this._isLoading)
constructor(
private dependency = injectService(SERVICE_TOKENS.DEPENDENCY)
) {
super()
}
async initialize(): Promise<void> {
// Initialization logic
await this.loadInitialData()
this.isInitialized.value = true
}
async dispose(): Promise<void> {
// Cleanup logic
this._data.value = []
this.isDisposed.value = true
}
// Public methods
async createItem(item: CreateItemRequest): Promise<MyData> {
this._isLoading.value = true
try {
const newItem = await this.dependency.create(item)
this._data.value.push(newItem)
return newItem
} finally {
this._isLoading.value = false
}
}
}
```
#### 2. Service Token Registration
```typescript
// Add to SERVICE_TOKENS
export const SERVICE_TOKENS = {
// ... existing tokens
MY_SERVICE: Symbol('MY_SERVICE') as InjectionKey<MyService>,
} as const
```
#### 3. Service Registration in Module
```typescript
// In module installation
const myService = new MyService()
container.provide(SERVICE_TOKENS.MY_SERVICE, myService)
await myService.initialize()
```
### **Service Best Practices**
#### **Reactive State Management**
- Use `ref()` for mutable state, `readonly()` for public access
- Provide computed properties for derived state
- Use `watch()` and `watchEffect()` for side effects
#### **Error Handling**
- Throw descriptive errors with proper types
- Use try/catch blocks with proper cleanup
- Log errors appropriately for debugging
#### **Performance Optimization**
- Implement proper subscription cleanup in `dispose()`
- Use debouncing for frequent operations
- Cache expensive computations with `computed()`
## Testing Services
### **Service Unit Tests**
```typescript
// tests/unit/services/MyService.test.ts
describe('MyService', () => {
let service: MyService
let mockDependency: MockType<DependencyService>
beforeEach(() => {
// Setup mocks
mockDependency = createMockService()
// Create service with mocks
service = new MyService(mockDependency)
})
afterEach(async () => {
await service.dispose()
})
it('should initialize correctly', async () => {
await service.initialize()
expect(service.isInitialized.value).toBe(true)
expect(service.data.value).toEqual([])
})
it('should create items', async () => {
await service.initialize()
const item = await service.createItem({ name: 'test' })
expect(service.data.value).toContain(item)
expect(mockDependency.create).toHaveBeenCalledWith({ name: 'test' })
})
})
```
### **Integration Tests**
```typescript
// tests/integration/services/ServiceIntegration.test.ts
describe('Service Integration', () => {
let container: DIContainer
beforeEach(async () => {
container = createTestContainer()
await installTestServices(container)
})
it('should handle cross-service communication', async () => {
const authService = container.get(SERVICE_TOKENS.AUTH_SERVICE)
const chatService = container.get(SERVICE_TOKENS.CHAT_SERVICE)
await authService.login('test-key')
const message = await chatService.sendMessage('Hello')
expect(message.author).toBe(authService.user.value?.pubkey)
})
})
```
## See Also
### Service Documentation
- **[[authentication|🔐 Authentication Service]]** - User identity and session management
- **[[payment-service|💳 Payment Service]]** - Lightning payment processing and wallet management
- **[[storage-service|💾 Storage Service]]** - User-scoped data persistence
- **[[toast-service|📢 Toast Service]]** - User notifications and feedback
- **[[visibility-service|👁️ Visibility Service]]** - Dynamic UI component control
- **[[../02-modules/wallet-module/index|💰 Wallet Services]]** - WalletService and WebSocket integration
### Architecture References
- **[[../01-architecture/dependency-injection|⚙️ Dependency Injection]]** - DI container system
- **[[../01-architecture/event-bus|📡 Event Bus Communication]]** - Inter-service messaging
- **[[../02-modules/index|📦 Module System]]** - How services integrate with modules
- **[[../04-development/testing|🧪 Testing Guide]]** - Service testing patterns
---
**Tags:** #services #architecture #dependency-injection #reactive-state
**Last Updated:** 2025-09-06
**Author:** Development Team