webapp/src/core/services/StorageService.ts
padreug 3cf10b1db4 1.3.4 User-Scoped Storage Pattern: Add StorageService integration across modules for improved data management
- Introduced STORAGE_SERVICE token in the DI container for consistent service registration.
- Updated BaseService to include storageService as a dependency, ensuring proper initialization and error handling.
- Refactored ChatService to utilize storageService for managing unread messages and peers, replacing localStorage usage.
- Enhanced MarketStore to save and load orders using storageService, improving data persistence and user experience.
- Registered storageService in the base module, ensuring it is initialized and disposed of correctly.

This integration streamlines data handling across the application, promoting better maintainability and consistency.
2025-09-06 12:08:39 +02:00

202 lines
No EOL
5.5 KiB
TypeScript

import { BaseService } from '@/core/base/BaseService'
/**
* Centralized storage service providing user-scoped localStorage operations
*
* Features:
* - Automatic user-scoped key generation
* - Type-safe storage operations
* - Consistent data isolation between users
* - Fallback to global storage for anonymous users
* - Error handling with graceful degradation
*/
export class StorageService extends BaseService {
// Service metadata
protected readonly metadata = {
name: 'StorageService',
version: '1.0.0',
dependencies: ['AuthService'] // Depends on auth for user pubkey
}
/**
* Service-specific initialization (called by BaseService)
*/
protected async onInitialize(): Promise<void> {
this.debug('StorageService initialized')
}
/**
* Generate user-scoped storage key
* @param baseKey - Base key to scope to current user
* @returns User-scoped key or base key if no user
*/
private getUserStorageKey(baseKey: string): string {
if (!this.authService?.user?.value?.pubkey) {
// No user authenticated, use global key
return baseKey
}
const userPubkey = this.authService.user.value.pubkey
return `${baseKey}_${userPubkey}`
}
/**
* Store user-scoped data in localStorage
* @param key - Base storage key
* @param data - Data to store (will be JSON serialized)
*/
setUserData<T>(key: string, data: T): void {
try {
const storageKey = this.getUserStorageKey(key)
const serializedData = JSON.stringify(data)
localStorage.setItem(storageKey, serializedData)
this.debug(`Stored user data: ${storageKey}`)
} catch (error) {
console.error(`Failed to store user data for key "${key}":`, error)
}
}
/**
* Retrieve user-scoped data from localStorage
* @param key - Base storage key
* @param defaultValue - Default value if not found
* @returns Stored data or default value
*/
getUserData<T>(key: string, defaultValue?: T): T | undefined {
try {
const storageKey = this.getUserStorageKey(key)
const stored = localStorage.getItem(storageKey)
if (stored === null) {
this.debug(`No stored data found for key: ${storageKey}`)
return defaultValue
}
const parsed = JSON.parse(stored) as T
this.debug(`Retrieved user data: ${storageKey}`)
return parsed
} catch (error) {
console.error(`Failed to retrieve user data for key "${key}":`, error)
return defaultValue
}
}
/**
* Remove user-scoped data from localStorage
* @param key - Base storage key
*/
clearUserData(key: string): void {
try {
const storageKey = this.getUserStorageKey(key)
localStorage.removeItem(storageKey)
this.debug(`Cleared user data: ${storageKey}`)
} catch (error) {
console.error(`Failed to clear user data for key "${key}":`, error)
}
}
/**
* Check if user-scoped data exists
* @param key - Base storage key
* @returns True if data exists
*/
hasUserData(key: string): boolean {
try {
const storageKey = this.getUserStorageKey(key)
return localStorage.getItem(storageKey) !== null
} catch (error) {
console.error(`Failed to check user data for key "${key}":`, error)
return false
}
}
/**
* Get all keys for the current user (useful for cleanup)
* @returns Array of storage keys belonging to current user
*/
getUserKeys(): string[] {
try {
if (!this.authService?.user?.value?.pubkey) {
return []
}
const userPubkey = this.authService.user.value.pubkey
const userKeys: string[] = []
// Scan localStorage for keys belonging to this user
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i)
if (key && key.endsWith(`_${userPubkey}`)) {
userKeys.push(key)
}
}
return userKeys
} catch (error) {
console.error('Failed to get user keys:', error)
return []
}
}
/**
* Clear all data for the current user (useful on logout)
*/
clearAllUserData(): void {
try {
const userKeys = this.getUserKeys()
for (const key of userKeys) {
localStorage.removeItem(key)
}
this.debug(`Cleared all user data: ${userKeys.length} keys`)
} catch (error) {
console.error('Failed to clear all user data:', error)
}
}
/**
* Migrate data from old storage pattern to user-scoped pattern
* @param oldKey - Old global key
* @param newKey - New base key for user scoping
*/
migrateToUserScoped<T>(oldKey: string, newKey: string): void {
try {
// Only migrate if we have a user and old data exists
if (!this.authService?.user?.value?.pubkey) {
return
}
const oldData = localStorage.getItem(oldKey)
if (oldData) {
const parsed = JSON.parse(oldData) as T
this.setUserData(newKey, parsed)
localStorage.removeItem(oldKey)
this.debug(`Migrated data from "${oldKey}" to user-scoped "${newKey}"`)
}
} catch (error) {
console.error(`Failed to migrate data from "${oldKey}" to "${newKey}":`, error)
}
}
/**
* Get current user pubkey (for debugging)
*/
getCurrentUserPubkey(): string | null {
return this.authService?.user?.value?.pubkey || null
}
/**
* Cleanup when service is disposed (called by BaseService)
*/
protected async onDispose(): Promise<void> {
this.debug('StorageService disposed')
}
}
// Export singleton instance
export const storageService = new StorageService()