You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

35 KiB

Secure Storage Implementation Guide for TimeSafari App

Overview

This document outlines the implementation of secure storage for the TimeSafari app using Capacitor solutions. The implementation focuses on:

  1. Platform-Specific Storage Solutions:

    • Web: wa-sqlite with IndexedDB backend
    • iOS: SQLCipher with Keychain integration
    • Android: SQLCipher with Keystore integration
    • Electron: SQLite with secure storage
  2. Key Features:

    • Encrypted storage using SQLCipher
    • Platform-specific security features
    • Migration support from existing implementations
    • Consistent API across platforms

Quick Start

1. Installation

# Core dependencies
npm install @capacitor-community/sqlite@6.0.0
npm install @wa-sqlite/sql.js@0.8.12
npm install @wa-sqlite/sql.js-httpvfs@0.8.12

# Platform-specific dependencies
npm install @capacitor/preferences@6.0.2
npm install @capacitor-community/biometric-auth@5.0.0

2. Basic Usage

// src/services/storage/StorageService.ts
import { PlatformServiceFactory } from '../PlatformServiceFactory';
import { StorageError, StorageErrorCodes } from './errors/StorageError';

export class StorageService {
  private static instance: StorageService;
  private platformService: PlatformService;

  private constructor() {
    this.platformService = PlatformServiceFactory.create();
  }

  static getInstance(): StorageService {
    if (!StorageService.instance) {
      StorageService.instance = new StorageService();
    }
    return StorageService.instance;
  }

  async initialize(): Promise<void> {
    try {
      // Initialize databases
      await this.platformService.openSecretDatabase();
      await this.platformService.openAccountsDatabase();

      // Check for migration
      if (await this.platformService.needsMigration()) {
        await this.handleMigration();
      }
    } catch (error) {
      throw new StorageError(
        'Failed to initialize storage service',
        StorageErrorCodes.INITIALIZATION_FAILED,
        error
      );
    }
  }

  private async handleMigration(): Promise<void> {
    try {
      // Show migration UI
      const shouldMigrate = await this.showMigrationPrompt();
      if (!shouldMigrate) return;

      // Perform migration
      await this.platformService.performMigration();
      
      // Verify migration
      await this.verifyMigration();
    } catch (error) {
      // Handle migration failure
      await this.handleMigrationError(error);
    }
  }

  // Example: Adding an account
  async addAccount(account: Account): Promise<void> {
    try {
      await this.platformService.addAccount(account);
    } catch (error) {
      if (error instanceof StorageError) {
        throw error;
      }
      throw new StorageError(
        'Failed to add account',
        StorageErrorCodes.QUERY_FAILED,
        error
      );
    }
  }

  // Example: Retrieving an account
  async getAccountByDid(did: string): Promise<Account | undefined> {
    try {
      return await this.platformService.getAccountByDid(did);
    } catch (error) {
      if (error instanceof StorageError) {
        throw error;
      }
      throw new StorageError(
        'Failed to retrieve account',
        StorageErrorCodes.QUERY_FAILED,
        error
      );
    }
  }
}

// Usage example:
const storageService = StorageService.getInstance();
await storageService.initialize();

try {
  const account = await storageService.getAccountByDid('did:example:123');
  if (!account) {
    await storageService.addAccount({
      did: 'did:example:123',
      publicKeyHex: '0x123...',
      // ... other account properties
    });
  }
} catch (error) {
  if (error instanceof StorageError) {
    console.error(`Storage error: ${error.code}`, error.message);
  } else {
    console.error('Unexpected error:', error);
  }
}

3. Platform Detection

// src/services/storage/PlatformDetection.ts
import { Capacitor } from '@capacitor/core';
import { StorageError, StorageErrorCodes } from './errors/StorageError';

export class PlatformDetection {
  static isNativePlatform(): boolean {
    return Capacitor.isNativePlatform();
  }

  static getPlatform(): 'ios' | 'android' | 'web' | 'electron' {
    if (Capacitor.isNativePlatform()) {
      return Capacitor.getPlatform() as 'ios' | 'android';
    }
    return window.electron ? 'electron' : 'web';
  }

  static async getCapabilities(): Promise<PlatformCapabilities> {
    try {
      const platform = this.getPlatform();
      
      return {
        hasFileSystem: platform !== 'web',
        hasSecureStorage: platform !== 'web',
        hasBiometrics: await this.checkBiometrics(),
        isIOS: platform === 'ios',
        isAndroid: platform === 'android'
      };
    } catch (error) {
      throw new StorageError(
        'Failed to detect platform capabilities',
        StorageErrorCodes.INITIALIZATION_FAILED,
        error
      );
    }
  }

  private static async checkBiometrics(): Promise<boolean> {
    if (!this.isNativePlatform()) return false;
    
    try {
      const { BiometricAuth } = await import('@capacitor-community/biometric-auth');
      const available = await BiometricAuth.isAvailable();
      return available.has;
    } catch (error) {
      console.warn('Biometric check failed:', error);
      return false;
    }
  }
}

// Usage example:
try {
  const capabilities = await PlatformDetection.getCapabilities();
  if (capabilities.hasSecureStorage) {
    // Use platform-specific secure storage
    await initializeSecureStorage();
  } else {
    // Fall back to web storage
    await initializeWebStorage();
  }
} catch (error) {
  if (error instanceof StorageError) {
    console.error(`Platform detection error: ${error.code}`, error.message);
  } else {
    console.error('Unexpected error during platform detection:', error);
  }
}

4. Platform-Specific Implementations

Web Platform (wa-sqlite)

// src/services/platforms/web/WebSQLiteService.ts
export class WebSQLiteService implements PlatformService {
  private db: SQLite.Database | null = null;
  private vfs: IDBBatchAtomicVFS | null = null;
  private initialized = false;

  async initialize(): Promise<void> {
    if (this.initialized) return;

    try {
      // 1. Initialize SQLite
      const sqlite3 = await this.initializeSQLite();
      
      // 2. Set up VFS
      await this.setupVFS(sqlite3);
      
      // 3. Open database
      await this.openDatabase();
      
      // 4. Set up schema
      await this.setupSchema();
      
      // 5. Optimize performance
      await this.optimizePerformance();
      
      this.initialized = true;
    } catch (error) {
      throw new StorageError(
        'Failed to initialize web SQLite',
        StorageErrorCodes.INITIALIZATION_FAILED,
        error
      );
    }
  }

  private async initializeSQLite(): Promise<SQLite.SqlJsStatic> {
    try {
      return await SQLite.init({
        locateFile: file => `https://cdn.jsdelivr.net/npm/@wa-sqlite/sql.js@0.8.12/dist/${file}`
      });
    } catch (error) {
      throw new StorageError(
        'Failed to load SQLite WebAssembly',
        StorageErrorCodes.WASM_LOAD_FAILED,
        error
      );
    }
  }

  private async setupVFS(sqlite3: SQLite.SqlJsStatic): Promise<void> {
    try {
      this.vfs = new IDBBatchAtomicVFS('timesafari');
      await this.vfs.registerVFS(sqlite3);
    } catch (error) {
      throw new StorageError(
        'Failed to set up IndexedDB VFS',
        StorageErrorCodes.VFS_SETUP_FAILED,
        error
      );
    }
  }

  async openDatabase(): Promise<void> {
    if (!this.vfs) {
      throw new StorageError(
        'VFS not initialized',
        StorageErrorCodes.INITIALIZATION_FAILED
      );
    }

    try {
      this.db = await this.vfs.openDatabase('timesafari.db');
      await this.setupPragmas();
    } catch (error) {
      throw new StorageError(
        'Failed to open database',
        StorageErrorCodes.INITIALIZATION_FAILED,
        error
      );
    }
  }

  private async setupPragmas(): Promise<void> {
    if (!this.db) return;

    try {
      await this.db.exec(`
        PRAGMA journal_mode = WAL;
        PRAGMA synchronous = NORMAL;
        PRAGMA foreign_keys = ON;
        PRAGMA busy_timeout = 5000;
      `);
    } catch (error) {
      throw new StorageError(
        'Failed to set up database pragmas',
        StorageErrorCodes.INITIALIZATION_FAILED,
        error
      );
    }
  }

  async close(): Promise<void> {
    if (this.db) {
      await this.db.close();
      this.db = null;
    }
    this.initialized = false;
  }
}

// Migration strategy for web platform
export class WebMigrationService {
  async migrate(): Promise<void> {
    // 1. Check prerequisites
    await this.checkPrerequisites();
    
    // 2. Create backup
    const backup = await this.createBackup();
    
    // 3. Perform migration
    try {
      await this.performMigration(backup);
    } catch (error) {
      // 4. Handle failure
      await this.handleMigrationFailure(error, backup);
    }
    
    // 5. Verify migration
    await this.verifyMigration(backup);
  }

  private async checkPrerequisites(): Promise<void> {
    // Check IndexedDB availability
    if (!window.indexedDB) {
      throw new StorageError(
        'IndexedDB not available',
        StorageErrorCodes.INITIALIZATION_FAILED
      );
    }

    // Check storage quota
    const quota = await navigator.storage.estimate();
    if (quota.quota && quota.usage && quota.usage > quota.quota * 0.9) {
      throw new StorageError(
        'Insufficient storage space',
        StorageErrorCodes.INITIALIZATION_FAILED
      );
    }
  }

  private async createBackup(): Promise<MigrationBackup> {
    const backup = {
      timestamp: Date.now(),
      accounts: await this.dexieDB.accounts.toArray(),
      settings: await this.dexieDB.settings.toArray(),
      contacts: await this.dexieDB.contacts.toArray()
    };

    // Store backup in IndexedDB
    await this.storeBackup(backup);
    
    return backup;
  }
}

Native Platform (iOS/Android)

// src/services/platforms/native/NativeSQLiteService.ts
export class NativeSQLiteService implements PlatformService {
  private db: SQLiteConnection | null = null;
  private initialized = false;

  async initialize(): Promise<void> {
    if (this.initialized) return;

    try {
      // 1. Check platform capabilities
      await this.checkPlatformCapabilities();
      
      // 2. Initialize SQLite with encryption
      await this.initializeEncryptedDatabase();
      
      // 3. Set up schema
      await this.setupSchema();
      
      this.initialized = true;
    } catch (error) {
      throw new StorageError(
        'Failed to initialize native SQLite',
        StorageErrorCodes.INITIALIZATION_FAILED,
        error
      );
    }
  }

  private async checkPlatformCapabilities(): Promise<void> {
    const { Capacitor } = await import('@capacitor/core');
    if (!Capacitor.isNativePlatform()) {
      throw new StorageError(
        'Not running on native platform',
        StorageErrorCodes.INITIALIZATION_FAILED
      );
    }
  }

  private async initializeEncryptedDatabase(): Promise<void> {
    const { SQLite } = await import('@capacitor-community/sqlite');
    this.db = await SQLite.createConnection(
      'timesafari',
      false,
      'encryption',
      1,
      false
    );
    await this.db.open();
  }

  async setupSchema(): Promise<void> {
    if (!this.db) {
      throw new StorageError(
        'Database not initialized',
        StorageErrorCodes.INITIALIZATION_FAILED
      );
    }

    try {
      await this.db.execute(`
        CREATE TABLE IF NOT EXISTS accounts (
          did TEXT PRIMARY KEY,
          public_key_hex TEXT NOT NULL,
          created_at INTEGER NOT NULL,
          updated_at INTEGER NOT NULL
        );

        CREATE TABLE IF NOT EXISTS settings (
          key TEXT PRIMARY KEY,
          value TEXT NOT NULL,
          updated_at INTEGER NOT NULL
        );

        CREATE TABLE IF NOT EXISTS contacts (
          did TEXT PRIMARY KEY,
          name TEXT NOT NULL,
          public_key_hex TEXT NOT NULL,
          created_at INTEGER NOT NULL,
          updated_at INTEGER NOT NULL
        );
      `);
    } catch (error) {
      throw new StorageError(
        'Failed to set up database schema',
        StorageErrorCodes.INITIALIZATION_FAILED,
        error
      );
    }
  }

  async close(): Promise<void> {
    if (this.db) {
      await this.db.close();
      this.db = null;
    }
    this.initialized = false;
  }
}

5. Error Handling

// src/services/storage/errors/StorageError.ts
export enum StorageErrorCodes {
  INITIALIZATION_FAILED = 'STORAGE_INIT_FAILED',
  QUERY_FAILED = 'STORAGE_QUERY_FAILED',
  MIGRATION_FAILED = 'STORAGE_MIGRATION_FAILED',
  ENCRYPTION_FAILED = 'STORAGE_ENCRYPTION_FAILED',
  DECRYPTION_FAILED = 'STORAGE_DECRYPTION_FAILED',
  INVALID_DATA = 'STORAGE_INVALID_DATA',
  DATABASE_CORRUPTED = 'STORAGE_DB_CORRUPTED',
  INSUFFICIENT_PERMISSIONS = 'STORAGE_INSUFFICIENT_PERMISSIONS',
  STORAGE_FULL = 'STORAGE_FULL',
  CONCURRENT_ACCESS = 'STORAGE_CONCURRENT_ACCESS'
}

export class StorageError extends Error {
  constructor(
    message: string,
    public code: StorageErrorCodes,
    public originalError?: unknown
  ) {
    super(message);
    this.name = 'StorageError';
  }

  static isStorageError(error: unknown): error is StorageError {
    return error instanceof StorageError;
  }

  static fromUnknown(error: unknown, context: string): StorageError {
    if (this.isStorageError(error)) {
      return error;
    }
    return new StorageError(
      `${context}: ${error instanceof Error ? error.message : String(error)}`,
      StorageErrorCodes.QUERY_FAILED,
      error
    );
  }
}

// Error recovery strategies
export class StorageErrorRecovery {
  static async handleError(error: StorageError): Promise<void> {
    switch (error.code) {
      case StorageErrorCodes.DATABASE_CORRUPTED:
        await this.handleCorruptedDatabase();
        break;
      case StorageErrorCodes.STORAGE_FULL:
        await this.handleStorageFull();
        break;
      case StorageErrorCodes.CONCURRENT_ACCESS:
        await this.handleConcurrentAccess();
        break;
      default:
        throw error; // Re-throw unhandled errors
    }
  }

  private static async handleCorruptedDatabase(): Promise<void> {
    // 1. Attempt to repair
    try {
      await this.repairDatabase();
    } catch {
      // 2. If repair fails, restore from backup
      await this.restoreFromBackup();
    }
  }

  private static async handleStorageFull(): Promise<void> {
    // 1. Clean up temporary files
    await this.cleanupTempFiles();
    
    // 2. If still full, notify user
    const isStillFull = await this.checkStorageFull();
    if (isStillFull) {
      throw new StorageError(
        'Storage is full. Please free up space.',
        StorageErrorCodes.STORAGE_FULL
      );
    }
  }

  private static async handleConcurrentAccess(): Promise<void> {
    // Implement retry logic with exponential backoff
    await this.retryWithBackoff(async () => {
      // Attempt operation again
    });
  }
}

6. Testing Strategy

// src/services/storage/__tests__/StorageService.test.ts
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { StorageService } from '../StorageService';
import { StorageError, StorageErrorCodes } from '../errors/StorageError';
import { PlatformDetection } from '../PlatformDetection';

describe('StorageService', () => {
  let storageService: StorageService;

  beforeEach(async () => {
    storageService = StorageService.getInstance();
    await storageService.initialize();
  });

  afterEach(async () => {
    // Clean up test data
    await cleanupTestData();
  });

  describe('Account Operations', () => {
    it('should add and retrieve an account', async () => {
      const account = {
        did: 'did:test:123',
        publicKeyHex: '0x123...',
        // ... other properties
      };

      await storageService.addAccount(account);
      const retrieved = await storageService.getAccountByDid(account.did);
      
      expect(retrieved).toBeDefined();
      expect(retrieved?.did).toBe(account.did);
    });

    it('should handle duplicate accounts', async () => {
      const account = {
        did: 'did:test:123',
        publicKeyHex: '0x123...',
      };

      await storageService.addAccount(account);
      
      await expect(
        storageService.addAccount(account)
      ).rejects.toThrow(StorageError);
    });
  });

  describe('Error Handling', () => {
    it('should handle database corruption', async () => {
      // Simulate database corruption
      await simulateDatabaseCorruption();

      await expect(
        storageService.getAccountByDid('did:test:123')
      ).rejects.toThrow(StorageError);

      // Verify recovery
      const recovered = await storageService.getAccountByDid('did:test:123');
      expect(recovered).toBeDefined();
    });

    it('should handle concurrent access', async () => {
      const promises = Array(5).fill(null).map(() =>
        storageService.addAccount({
          did: `did:test:${Math.random()}`,
          publicKeyHex: '0x123...',
        })
      );

      const results = await Promise.allSettled(promises);
      const errors = results.filter(r => r.status === 'rejected');
      
      expect(errors.length).toBeLessThan(promises.length);
    });
  });

  describe('Platform-Specific Tests', () => {
    it('should use correct storage implementation', async () => {
      const capabilities = await PlatformDetection.getCapabilities();
      
      if (capabilities.hasSecureStorage) {
        // Verify native storage implementation
        expect(storageService.getImplementation()).toBe('native');
      } else {
        // Verify web storage implementation
        expect(storageService.getImplementation()).toBe('web');
      }
    });

    it('should handle platform transitions', async () => {
      // Simulate platform change (e.g., web to native)
      await simulatePlatformChange();

      // Verify data persistence
      const account = await storageService.getAccountByDid('did:test:123');
      expect(account).toBeDefined();
    });
  });
});

// Helper functions for testing
async function cleanupTestData(): Promise<void> {
  // Implementation
}

async function simulateDatabaseCorruption(): Promise<void> {
  // Implementation
}

async function simulatePlatformChange(): Promise<void> {
  // Implementation
}

Additional Platform-Specific Tests

// src/services/storage/__tests__/WebSQLiteService.spec.ts
import { WebSQLiteService } from '../platforms/web/WebSQLiteService';
import { StorageError, StorageErrorCodes } from '../errors/StorageError';

describe('WebSQLiteService', () => {
  let service: WebSQLiteService;

  beforeEach(async () => {
    service = new WebSQLiteService();
    await service.initialize();
  });

  afterEach(async () => {
    await service.close();
  });

  it('should initialize successfully', async () => {
    expect(service.isInitialized()).toBe(true);
  });

  it('should handle IndexedDB errors', async () => {
    // Mock IndexedDB failure
    const mockIndexedDB = jest.spyOn(window, 'indexedDB', 'get');
    mockIndexedDB.mockImplementation(() => undefined);
    
    await expect(service.initialize()).rejects.toThrow(
      new StorageError(
        'IndexedDB not available',
        StorageErrorCodes.INITIALIZATION_FAILED
      )
    );
  });

  it('should migrate data correctly', async () => {
    // Set up test data
    const testAccount = createTestAccount();
    await dexieDB.accounts.add(testAccount);

    // Perform migration
    await service.migrate();

    // Verify migration
    const migratedAccount = await service.getAccountByDid(testAccount.did);
    expect(migratedAccount).toEqual(testAccount);
  });
});

// Integration tests
describe('StorageService Integration', () => {
  it('should handle concurrent access', async () => {
    const service1 = StorageService.getInstance();
    const service2 = StorageService.getInstance();

    // Simulate concurrent access
    const [result1, result2] = await Promise.all([
      service1.addAccount(testAccount1),
      service2.addAccount(testAccount2)
    ]);

    // Verify both operations succeeded
    expect(result1).toBeDefined();
    expect(result2).toBeDefined();
  });

  it('should recover from errors', async () => {
    const service = StorageService.getInstance();

    // Simulate database corruption
    await simulateDatabaseCorruption();

    // Attempt recovery
    await service.recover();

    // Verify data integrity
    const accounts = await service.getAllAccounts();
    expect(accounts).toBeDefined();
  });
});

7. Troubleshooting Guide

Detailed Recovery Procedures

  1. Database Corruption Recovery

    async recoverFromCorruption(): Promise<void> {
      // 1. Stop all database operations
      await this.stopDatabaseOperations();
    
      // 2. Create backup of corrupted database
      const backup = await this.createEmergencyBackup();
    
      // 3. Attempt repair
      try {
        await this.repairDatabase();
      } catch (error) {
        // 4. Restore from backup if repair fails
        await this.restoreFromBackup(backup);
      }
    }
    
  2. Migration Recovery

    async recoverFromFailedMigration(): Promise<void> {
      // 1. Identify migration stage
      const stage = await this.getMigrationStage();
    
      // 2. Execute appropriate recovery
      switch (stage) {
        case 'backup':
          await this.recoverFromBackupStage();
          break;
        case 'migration':
          await this.recoverFromMigrationStage();
          break;
        case 'verification':
          await this.recoverFromVerificationStage();
          break;
      }
    }
    
  3. Performance Troubleshooting

    • Monitor database size and growth
    • Check query performance with EXPLAIN
    • Review indexing strategy
    • Monitor memory usage
    • Check for connection leaks

Success Criteria

  1. Functionality

    • All CRUD operations work correctly
    • Migration process completes successfully
    • Error handling works as expected
    • Platform-specific features function correctly
  2. Performance

    • Database operations complete within acceptable time
    • Memory usage remains stable
    • IndexedDB quota usage is monitored
    • Concurrent operations work correctly
  3. Security

    • Data is properly encrypted
    • Keys are securely stored
    • Platform-specific security features work
    • No sensitive data leaks
  4. Testing

    • All unit tests pass
    • Integration tests complete successfully
    • Edge cases are handled
    • Error recovery works as expected

Appendix

A. Database Schema

-- Accounts Table
CREATE TABLE accounts (
  did TEXT PRIMARY KEY,
  public_key_hex TEXT NOT NULL,
  created_at INTEGER NOT NULL,
  updated_at INTEGER NOT NULL
);

-- Settings Table
CREATE TABLE settings (
  key TEXT PRIMARY KEY,
  value TEXT NOT NULL,
  updated_at INTEGER NOT NULL
);

-- Contacts Table
CREATE TABLE contacts (
  did TEXT PRIMARY KEY,
  name TEXT NOT NULL,
  public_key_hex TEXT NOT NULL,
  created_at INTEGER NOT NULL,
  updated_at INTEGER NOT NULL
);

-- Indexes
CREATE INDEX idx_accounts_created_at ON accounts(created_at);
CREATE INDEX idx_contacts_created_at ON contacts(created_at);
CREATE INDEX idx_settings_updated_at ON settings(updated_at);

B. Error Codes Reference

Code Description Recovery Action
STORAGE_INIT_FAILED Database initialization failed Check permissions, storage space
STORAGE_QUERY_FAILED Database query failed Verify query, check connection
STORAGE_MIGRATION_FAILED Data migration failed Use backup, manual migration
STORAGE_ENCRYPTION_FAILED Data encryption failed Check key management
STORAGE_DECRYPTION_FAILED Data decryption failed Verify encryption key
STORAGE_INVALID_DATA Invalid data format Validate input data
STORAGE_DB_CORRUPTED Database corruption detected Use backup, repair
STORAGE_INSUFFICIENT_PERMISSIONS Missing required permissions Request permissions
STORAGE_FULL Storage quota exceeded Clean up, increase quota
STORAGE_CONCURRENT_ACCESS Concurrent access conflict Implement retry logic

C. Platform Capabilities Matrix

Feature Web iOS Android Electron
SQLite wa-sqlite SQLCipher SQLCipher SQLite
Encryption SQLCipher SQLCipher SQLCipher SQLCipher
Secure Storage IndexedDB Keychain Keystore Secure Storage
Biometrics No Yes Yes No
File System Limited Full Full Full
Background Sync No Yes Yes Yes
Storage Quota Yes No No No
Multi-tab Support Yes N/A N/A Yes

D. Usage Examples

Before (Using Dexie.js)

// src/services/storage/legacy/AccountService.ts
import Dexie from 'dexie';

class AccountDatabase extends Dexie {
  accounts: Dexie.Table<Account, string>;
  settings: Dexie.Table<Setting, string>;
  contacts: Dexie.Table<Contact, string>;

  constructor() {
    super('TimeSafariDB');
    this.version(1).stores({
      accounts: 'did, publicKeyHex, createdAt, updatedAt',
      settings: 'key, value, updatedAt',
      contacts: 'did, name, publicKeyHex, createdAt, updatedAt'
    });
  }
}

export class AccountService {
  private db: AccountDatabase;

  constructor() {
    this.db = new AccountDatabase();
  }

  // Account Management
  async addAccount(account: Account): Promise<void> {
    try {
      await this.db.accounts.add(account);
    } catch (error) {
      if (error instanceof Dexie.ConstraintError) {
        throw new Error('Account already exists');
      }
      throw error;
    }
  }

  async getAccount(did: string): Promise<Account | undefined> {
    return await this.db.accounts.get(did);
  }

  // Settings Management
  async updateSetting(key: string, value: string): Promise<void> {
    await this.db.settings.put({
      key,
      value,
      updatedAt: Date.now()
    });
  }

  async getSetting(key: string): Promise<string | undefined> {
    const setting = await this.db.settings.get(key);
    return setting?.value;
  }

  // Contact Management
  async addContact(contact: Contact): Promise<void> {
    await this.db.contacts.add(contact);
  }

  async getContacts(): Promise<Contact[]> {
    return await this.db.contacts.toArray();
  }
}

// Usage Example
const accountService = new AccountService();

// Add an account
await accountService.addAccount({
  did: 'did:example:123',
  publicKeyHex: '0x123...',
  createdAt: Date.now(),
  updatedAt: Date.now()
});

// Update settings
await accountService.updateSetting('theme', 'dark');

// Add a contact
await accountService.addContact({
  did: 'did:example:456',
  name: 'Alice',
  publicKeyHex: '0x456...',
  createdAt: Date.now(),
  updatedAt: Date.now()
});

After (Using Platform Service)

// src/services/storage/AccountService.ts
import { StorageService } from './StorageService';
import { StorageError, StorageErrorCodes } from './errors/StorageError';
import { PlatformDetection } from './PlatformDetection';

export class AccountService {
  private static instance: AccountService;
  private storageService: StorageService;

  private constructor() {
    this.storageService = StorageService.getInstance();
  }

  static getInstance(): AccountService {
    if (!AccountService.instance) {
      AccountService.instance = new AccountService();
    }
    return AccountService.instance;
  }

  async initialize(): Promise<void> {
    try {
      // Initialize storage with platform-specific implementation
      await this.storageService.initialize();

      // Check for migration if needed
      if (await this.storageService.needsMigration()) {
        await this.handleMigration();
      }
    } catch (error) {
      throw StorageError.fromUnknown(error, 'Failed to initialize account service');
    }
  }

  // Account Management with Platform-Specific Features
  async addAccount(account: Account): Promise<void> {
    try {
      // Check platform capabilities
      const capabilities = await PlatformDetection.getCapabilities();
      
      // Add platform-specific metadata
      const enhancedAccount = {
        ...account,
        platform: capabilities.isIOS ? 'ios' : 
                 capabilities.isAndroid ? 'android' : 
                 capabilities.hasSecureStorage ? 'electron' : 'web',
        secureStorage: capabilities.hasSecureStorage,
        biometricsEnabled: capabilities.hasBiometrics
      };

      await this.storageService.addAccount(enhancedAccount);

      // If platform supports biometrics, offer to enable it
      if (capabilities.hasBiometrics) {
        await this.offerBiometricSetup(account.did);
      }
    } catch (error) {
      if (error instanceof StorageError) {
        throw error;
      }
      throw new StorageError(
        'Failed to add account',
        StorageErrorCodes.QUERY_FAILED,
        error
      );
    }
  }

  async getAccount(did: string): Promise<Account | undefined> {
    try {
      const account = await this.storageService.getAccountByDid(did);
      
      // Verify account integrity
      if (account) {
        await this.verifyAccountIntegrity(account);
      }
      
      return account;
    } catch (error) {
      throw StorageError.fromUnknown(error, `Failed to get account ${did}`);
    }
  }

  // Settings Management with Encryption
  async updateSetting(key: string, value: string): Promise<void> {
    try {
      const capabilities = await PlatformDetection.getCapabilities();
      
      // Encrypt sensitive settings if platform supports it
      const processedValue = capabilities.hasSecureStorage ? 
        await this.encryptSetting(value) : value;

      await this.storageService.updateSettings({
        key,
        value: processedValue,
        updatedAt: Date.now()
      });
    } catch (error) {
      throw StorageError.fromUnknown(error, `Failed to update setting ${key}`);
    }
  }

  async getSetting(key: string): Promise<string | undefined> {
    try {
      const setting = await this.storageService.getAccountSettings(key);
      
      if (setting?.value) {
        const capabilities = await PlatformDetection.getCapabilities();
        
        // Decrypt if the setting was encrypted
        return capabilities.hasSecureStorage ? 
          await this.decryptSetting(setting.value) : 
          setting.value;
      }
      
      return undefined;
    } catch (error) {
      throw StorageError.fromUnknown(error, `Failed to get setting ${key}`);
    }
  }

  // Contact Management with Platform Integration
  async addContact(contact: Contact): Promise<void> {
    try {
      const capabilities = await PlatformDetection.getCapabilities();
      
      // Add platform-specific features
      const enhancedContact = {
        ...contact,
        platform: capabilities.isIOS ? 'ios' : 
                 capabilities.isAndroid ? 'android' : 
                 capabilities.hasSecureStorage ? 'electron' : 'web',
        syncEnabled: capabilities.hasBackgroundSync
      };

      await this.storageService.addContact(enhancedContact);

      // If platform supports background sync, schedule contact sync
      if (capabilities.hasBackgroundSync) {
        await this.scheduleContactSync(contact.did);
      }
    } catch (error) {
      throw StorageError.fromUnknown(error, 'Failed to add contact');
    }
  }

  async getContacts(): Promise<Contact[]> {
    try {
      const contacts = await this.storageService.getAllContacts();
      
      // Verify contact data integrity
      await Promise.all(contacts.map(contact => 
        this.verifyContactIntegrity(contact)
      ));
      
      return contacts;
    } catch (error) {
      throw StorageError.fromUnknown(error, 'Failed to get contacts');
    }
  }

  // Platform-Specific Helper Methods
  private async offerBiometricSetup(did: string): Promise<void> {
    const { BiometricAuth } = await import('@capacitor-community/biometric-auth');
    const available = await BiometricAuth.isAvailable();
    
    if (available.has) {
      // Show biometric setup prompt
      // Implementation depends on UI framework
    }
  }

  private async verifyAccountIntegrity(account: Account): Promise<void> {
    // Verify account data integrity
    // Implementation depends on security requirements
  }

  private async verifyContactIntegrity(contact: Contact): Promise<void> {
    // Verify contact data integrity
    // Implementation depends on security requirements
  }

  private async encryptSetting(value: string): Promise<string> {
    // Encrypt sensitive settings
    // Implementation depends on encryption requirements
    return value; // Placeholder
  }

  private async decryptSetting(value: string): Promise<string> {
    // Decrypt sensitive settings
    // Implementation depends on encryption requirements
    return value; // Placeholder
  }

  private async scheduleContactSync(did: string): Promise<void> {
    // Schedule background sync for contacts
    // Implementation depends on platform capabilities
  }
}

// Usage Example
const accountService = AccountService.getInstance();

// Initialize with platform detection
await accountService.initialize();

try {
  // Add an account with platform-specific features
  await accountService.addAccount({
    did: 'did:example:123',
    publicKeyHex: '0x123...',
    createdAt: Date.now(),
    updatedAt: Date.now()
  });

  // Update settings with encryption if available
  await accountService.updateSetting('theme', 'dark');
  await accountService.updateSetting('apiKey', 'sensitive-data');

  // Add a contact with platform integration
  await accountService.addContact({
    did: 'did:example:456',
    name: 'Alice',
    publicKeyHex: '0x456...',
    createdAt: Date.now(),
    updatedAt: Date.now()
  });

  // Retrieve data with integrity verification
  const account = await accountService.getAccount('did:example:123');
  const contacts = await accountService.getContacts();
  const theme = await accountService.getSetting('theme');

  console.log('Account:', account);
  console.log('Contacts:', contacts);
  console.log('Theme:', theme);
} catch (error) {
  if (error instanceof StorageError) {
    console.error(`Storage error: ${error.code}`, error.message);
  } else {
    console.error('Unexpected error:', error);
  }
}

Key improvements in the new implementation:

  1. Platform Awareness:

    • Automatically detects platform capabilities
    • Uses platform-specific features (biometrics, secure storage)
    • Handles platform transitions gracefully
  2. Enhanced Security:

    • Encrypts sensitive data when platform supports it
    • Verifies data integrity
    • Uses platform-specific secure storage
  3. Better Error Handling:

    • Consistent error types and codes
    • Platform-specific error recovery
    • Detailed error messages
  4. Migration Support:

    • Automatic migration detection
    • Data integrity verification
    • Backup and recovery
  5. Platform Integration:

    • Background sync for contacts
    • Biometric authentication
    • Secure storage for sensitive data
  6. Type Safety:

    • Strong typing throughout
    • Platform capability type checking
    • Error type narrowing
  7. Singleton Pattern:

    • Single instance management
    • Consistent state across the app
    • Resource sharing
  8. Extensibility:

    • Easy to add new platform features
    • Modular design
    • Clear separation of concerns