diff --git a/doc/electron-migration.md b/doc/electron-migration.md new file mode 100644 index 00000000..ae132197 --- /dev/null +++ b/doc/electron-migration.md @@ -0,0 +1,270 @@ +# Electron App Migration Strategy + +## Overview + +This document outlines the migration strategy for the TimeSafari Electron app, focusing on the transition from web-based storage to native SQLite implementation while maintaining cross-platform compatibility. + +## Current Architecture + +### 1. Platform Services +- `ElectronPlatformService`: Implements platform-specific features for desktop +- Uses `@capacitor-community/sqlite` for database operations +- Maintains compatibility with web/mobile platforms through shared interfaces + +### 2. Database Implementation +- SQLite with native Node.js backend +- WAL journal mode for better concurrency +- Connection pooling for performance +- Migration system for schema updates +- Secure file permissions (0o755) + +### 3. Build Process +```bash +# Development +npm run dev:electron + +# Production Build +npm run build:web +npm run build:electron +npm run electron:build-linux # or electron:build-mac +``` + +## Migration Goals + +1. **Data Integrity** + - Preserve existing data during migration + - Maintain data relationships + - Ensure ACID compliance + - Implement proper backup/restore + +2. **Performance** + - Optimize SQLite configuration + - Implement connection pooling + - Use WAL journal mode + - Configure optimal PRAGMA settings + +3. **Security** + - Secure file permissions + - Proper IPC communication + - Context isolation + - Safe preload scripts + +4. **User Experience** + - Zero data loss + - Automatic migration + - Progress indicators + - Error recovery + +## Implementation Details + +### 1. Database Initialization +```typescript +// electron/src/rt/sqlite-init.ts +export async function initializeSQLite() { + // Set up database path with proper permissions + const dbPath = path.join(app.getPath('userData'), 'timesafari.db'); + + // Initialize SQLite plugin + const sqlite = new CapacitorSQLite(); + + // Configure database + await sqlite.createConnection({ + database: 'timesafari', + path: dbPath, + encrypted: false, + mode: 'no-encryption' + }); + + // Set optimal PRAGMA settings + await sqlite.execute({ + database: 'timesafari', + statements: [ + 'PRAGMA journal_mode = WAL;', + 'PRAGMA synchronous = NORMAL;', + 'PRAGMA foreign_keys = ON;' + ] + }); +} +``` + +### 2. Migration System +```typescript +// electron/src/rt/sqlite-migrations.ts +interface Migration { + version: number; + name: string; + description: string; + sql: string; + rollback?: string; +} + +async function runMigrations(plugin: any, database: string) { + // Track migration state + const state = await getMigrationState(plugin, database); + + // Execute migrations in transaction + for (const migration of pendingMigrations) { + await executeMigration(plugin, database, migration); + } +} +``` + +### 3. Platform Service Implementation +```typescript +// src/services/platforms/ElectronPlatformService.ts +export class ElectronPlatformService implements PlatformService { + private sqlite: any; + + async dbQuery(sql: string, params: any[]): Promise { + return await this.sqlite.execute({ + database: 'timesafari', + statements: [{ statement: sql, values: params }] + }); + } +} +``` + +### 4. Preload Script +```typescript +// electron/preload.ts +contextBridge.exposeInMainWorld('electron', { + sqlite: { + isAvailable: () => ipcRenderer.invoke('sqlite:isAvailable'), + execute: (method: string, ...args: unknown[]) => + ipcRenderer.invoke('sqlite:execute', method, ...args) + }, + getPath: (pathType: string) => ipcRenderer.invoke('get-path', pathType), + env: { + platform: 'electron' + } +}); +``` + +## Build Configuration + +### 1. Vite Configuration +```typescript +// vite.config.app.electron.mts +export default defineConfig({ + build: { + outDir: 'dist', + emptyOutDir: true + }, + define: { + 'process.env.VITE_PLATFORM': JSON.stringify('electron'), + 'process.env.VITE_PWA_ENABLED': JSON.stringify(false) + } +}); +``` + +### 2. Package Scripts +```json +{ + "scripts": { + "dev:electron": "vite build --watch --config vite.config.app.electron.mts", + "build:electron": "vite build --config vite.config.app.electron.mts", + "electron:build-linux": "electron-builder --linux", + "electron:build-mac": "electron-builder --mac" + } +} +``` + +## Testing Strategy + +1. **Unit Tests** + - Database operations + - Migration system + - Platform service methods + - IPC communication + +2. **Integration Tests** + - Full migration process + - Data integrity verification + - Cross-platform compatibility + - Error recovery + +3. **End-to-End Tests** + - User workflows + - Data persistence + - UI interactions + - Platform-specific features + +## Error Handling + +1. **Database Errors** + - Connection failures + - Migration errors + - Query execution errors + - Transaction failures + +2. **Platform Errors** + - File system errors + - IPC communication errors + - Permission issues + - Resource constraints + +3. **Recovery Mechanisms** + - Automatic retry logic + - Transaction rollback + - State verification + - User notifications + +## Security Considerations + +1. **File System** + - Secure file permissions + - Path validation + - Access control + - Data encryption + +2. **IPC Communication** + - Context isolation + - Channel validation + - Data sanitization + - Error handling + +3. **Preload Scripts** + - Minimal API exposure + - Type safety + - Input validation + - Error boundaries + +## Future Improvements + +1. **Performance** + - Query optimization + - Index tuning + - Connection management + - Cache implementation + +2. **Features** + - Offline support + - Sync capabilities + - Backup/restore + - Data export/import + +3. **Security** + - Database encryption + - Secure storage + - Access control + - Audit logging + +## Maintenance + +1. **Regular Tasks** + - Database optimization + - Log rotation + - Error monitoring + - Performance tracking + +2. **Updates** + - Dependency updates + - Security patches + - Feature additions + - Bug fixes + +3. **Documentation** + - API documentation + - Migration guides + - Troubleshooting + - Best practices \ No newline at end of file diff --git a/electron/src/rt/sqlite-migrations.ts b/electron/src/rt/sqlite-migrations.ts index 3ee1684d..5cb29c6b 100644 --- a/electron/src/rt/sqlite-migrations.ts +++ b/electron/src/rt/sqlite-migrations.ts @@ -353,9 +353,108 @@ const INITIAL_MIGRATION: Migration = { ` }; -// Migration registry +// Migration definitions const MIGRATIONS: Migration[] = [ - INITIAL_MIGRATION + INITIAL_MIGRATION, + { + version: 1, + name: 'initial_schema', + description: 'Initial database schema with accounts, secret, settings, contacts, logs, and temp tables', + sql: ` + -- Accounts table for user identities + CREATE TABLE IF NOT EXISTS accounts ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + dateCreated TEXT NOT NULL, + derivationPath TEXT, + did TEXT NOT NULL, + identityEncrBase64 TEXT, -- encrypted & base64-encoded + mnemonicEncrBase64 TEXT, -- encrypted & base64-encoded + passkeyCredIdHex TEXT, + publicKeyHex TEXT NOT NULL + ); + + CREATE INDEX IF NOT EXISTS idx_accounts_did ON accounts(did); + + -- Secret table for storing encryption keys + -- Note: This is a temporary solution until better secure storage is implemented + CREATE TABLE IF NOT EXISTS secret ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + secretBase64 TEXT NOT NULL + ); + + -- Settings table for user preferences and app state + CREATE TABLE IF NOT EXISTS settings ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + accountDid TEXT, + activeDid TEXT, + apiServer TEXT, + filterFeedByNearby BOOLEAN, + filterFeedByVisible BOOLEAN, + finishedOnboarding BOOLEAN, + firstName TEXT, + hideRegisterPromptOnNewContact BOOLEAN, + isRegistered BOOLEAN, + lastName TEXT, + lastAckedOfferToUserJwtId TEXT, + lastAckedOfferToUserProjectsJwtId TEXT, + lastNotifiedClaimId TEXT, + lastViewedClaimId TEXT, + notifyingNewActivityTime TEXT, + notifyingReminderMessage TEXT, + notifyingReminderTime TEXT, + partnerApiServer TEXT, + passkeyExpirationMinutes INTEGER, + profileImageUrl TEXT, + searchBoxes TEXT, -- Stored as JSON string + showContactGivesInline BOOLEAN, + showGeneralAdvanced BOOLEAN, + showShortcutBvc BOOLEAN, + vapid TEXT, + warnIfProdServer BOOLEAN, + warnIfTestServer BOOLEAN, + webPushServer TEXT + ); + + CREATE INDEX IF NOT EXISTS idx_settings_accountDid ON settings(accountDid); + + -- Contacts table for user connections + CREATE TABLE IF NOT EXISTS contacts ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + did TEXT NOT NULL, + name TEXT, + contactMethods TEXT, -- Stored as JSON string + nextPubKeyHashB64 TEXT, + notes TEXT, + profileImageUrl TEXT, + publicKeyBase64 TEXT, + seesMe BOOLEAN, + registered BOOLEAN + ); + + CREATE INDEX IF NOT EXISTS idx_contacts_did ON contacts(did); + CREATE INDEX IF NOT EXISTS idx_contacts_name ON contacts(name); + + -- Logs table for application logging + CREATE TABLE IF NOT EXISTS logs ( + date TEXT NOT NULL, + message TEXT NOT NULL + ); + + -- Temp table for temporary data storage + CREATE TABLE IF NOT EXISTS temp ( + id TEXT PRIMARY KEY, + blobB64 TEXT + ); + `, + rollback: ` + DROP TABLE IF EXISTS accounts; + DROP TABLE IF EXISTS secret; + DROP TABLE IF EXISTS settings; + DROP TABLE IF EXISTS contacts; + DROP TABLE IF EXISTS logs; + DROP TABLE IF EXISTS temp; + ` + } ]; // Helper functions