From fddb2ac9590e8968cc06d71fbb5dd3a0e1c269a1 Mon Sep 17 00:00:00 2001 From: Matthew Raymer Date: Fri, 29 Aug 2025 07:58:50 +0000 Subject: [PATCH] feat(migration)!: enhance ActiveDid migration plan with focused implementation - Add foreign key constraints to prevent data corruption - Implement comprehensive migration validation and rollback - Focus API updates on PlatformServiceMixin only (no component changes) - Add enhanced error handling and data integrity checks - Streamline plan to focus only on what needs to change - Update timestamps and implementation details for current state Breaking Changes: - Database schema requires new active_identity table with constraints - PlatformServiceMixin methods need updates for new table structure Migration Impact: - 50+ components work automatically through API layer - Only core database and API methods require changes - Comprehensive rollback procedures for risk mitigation --- doc/activeDid-migration-plan.md | 919 +++++++++++++------------------- 1 file changed, 356 insertions(+), 563 deletions(-) diff --git a/doc/activeDid-migration-plan.md b/doc/activeDid-migration-plan.md index 4f5afedd..12407172 100644 --- a/doc/activeDid-migration-plan.md +++ b/doc/activeDid-migration-plan.md @@ -1,19 +1,20 @@ # ActiveDid Migration Plan - Separate Table Architecture **Author**: Matthew Raymer -**Date**: 2025-01-27T18:30Z +**Date**: 2025-08-29T07:24Z **Status**: 🎯 **PLANNING** - Active migration planning phase ## Objective Move the `activeDid` field from the `settings` table to a dedicated -`active_identity` table to improve database architecture and separate -identity selection from user preferences. +`active_identity` table to improve database architecture, prevent data corruption, +and separate identity selection from user preferences. ## Result This document serves as the comprehensive planning and implementation -guide for the ActiveDid migration. +guide for the ActiveDid migration with enhanced data integrity and +rollback capabilities. ## Use/Run @@ -24,15 +25,17 @@ approach. ## Context & Scope - **In scope**: - - Database schema modification for active_identity table - - Migration of existing activeDid data - - Updates to all platform services and mixins + - Database schema modification for active_identity table with proper constraints + - Migration of existing activeDid data with validation + - Updates to PlatformServiceMixin API layer - Type definition updates - Testing across all platforms + - Comprehensive rollback procedures - **Out of scope**: - Changes to user interface for identity selection - Modifications to identity creation logic - Changes to authentication flow + - Updates to individual components (handled by API layer) ## Environment & Preconditions @@ -44,19 +47,22 @@ approach. ## Architecture / Process Overview The migration follows a phased approach to minimize risk and ensure -data integrity: +data integrity with enhanced validation and rollback capabilities: ```mermaid flowchart TD - A[Current State
activeDid in settings] --> B[Phase 1: Schema Creation
Add active_identity table] - B --> C[Phase 2: Data Migration
Copy activeDid data] - C --> D[Phase 3: API Updates
Update all access methods] + A[Current State
activeDid in settings] --> B[Phase 1: Schema Creation
Add active_identity table with constraints] + B --> C[Phase 2: Data Migration
Copy activeDid data with validation] + C --> D[Phase 3: API Updates
Update PlatformServiceMixin methods] D --> E[Phase 4: Cleanup
Remove activeDid from settings] E --> F[Final State
Separate active_identity table] - G[Rollback Plan
Keep old field until verified] --> H[Data Validation
Verify integrity at each step] + G[Enhanced Rollback Plan
Schema and data rollback] --> H[Data Validation
Verify integrity at each step] H --> I[Platform Testing
Test all platforms] - I --> J[Production Deployment
Gradual rollout] + I --> J[Production Deployment
Gradual rollout with monitoring] + + K[Foreign Key Constraints
Prevent future corruption] --> L[Performance Optimization
Proper indexing] + L --> M[Error Recovery
Graceful failure handling] ``` ## Interfaces & Contracts @@ -66,66 +72,154 @@ flowchart TD | Table | Current Schema | New Schema | Migration Required | |-------|----------------|------------|-------------------| | `settings` | `activeDid TEXT` | Field removed | Yes - data migration | -| `active_identity` | Does not exist | New table with `activeDid TEXT` | Yes - table creation | +| `active_identity` | Does not exist | New table with `activeDid TEXT` + constraints | Yes - table creation | -### API Contract Changes +### Enhanced API Contract Changes | Method | Current Behavior | New Behavior | Breaking Change | |---------|------------------|--------------|-----------------| -| `$accountSettings()` | Returns settings with activeDid | Returns settings without activeDid | No - backward compatible | +| `$accountSettings()` | Returns settings with activeDid | Returns settings with activeDid from new table | No - backward compatible | | `$saveSettings()` | Updates settings.activeDid | Updates active_identity.activeDid | Yes - requires updates | | `$updateActiveDid()` | Updates internal tracking | Updates active_identity table | Yes - requires updates | +| `$getActiveIdentity()` | Does not exist | New method for active identity management | No - new functionality | ## Repro: End-to-End Procedure -### Phase 1: Schema Creation +### Phase 1: Enhanced Schema Creation ```sql --- Create new active_identity table +-- Create new active_identity table with proper constraints CREATE TABLE active_identity ( id INTEGER PRIMARY KEY CHECK (id = 1), activeDid TEXT NOT NULL, - lastUpdated TEXT NOT NULL DEFAULT (datetime('now')) + lastUpdated TEXT NOT NULL DEFAULT (datetime('now')), + FOREIGN KEY (activeDid) REFERENCES accounts(did) ON DELETE CASCADE ); +-- Add performance indexes +CREATE INDEX IF NOT EXISTS idx_active_identity_activeDid ON active_identity(activeDid); +CREATE UNIQUE INDEX IF NOT EXISTS idx_active_identity_single_record ON active_identity(id); + -- Insert default record (will be updated during migration) -INSERT INTO active_identity (id, activeDid) VALUES (1, ''); +INSERT INTO active_identity (id, activeDid, lastUpdated) VALUES (1, '', datetime('now')); ``` -### Phase 2: Data Migration +### Phase 2: Enhanced Data Migration with Validation ```typescript -// Migration script to copy existing activeDid values -async function migrateActiveDidToSeparateTable(): Promise { - // Get current activeDid from settings - const currentSettings = await retrieveSettingsForDefaultAccount(); - const activeDid = currentSettings.activeDid; - - if (activeDid) { - // Insert into new table - await dbExec( - "UPDATE active_identity SET activeDid = ?, lastUpdated = datetime('now') WHERE id = 1", +// Enhanced migration script with comprehensive validation +async function migrateActiveDidToSeparateTable(): Promise { + const result: MigrationResult = { + success: false, + errors: [], + warnings: [], + dataMigrated: 0 + }; + + try { + // 1. Get current activeDid from settings + const currentSettings = await retrieveSettingsForDefaultAccount(); + const activeDid = currentSettings.activeDid; + + if (!activeDid) { + result.warnings.push("No activeDid found in current settings"); + return result; + } + + // 2. Validate activeDid exists in accounts table + const accountExists = await dbQuery( + "SELECT did FROM accounts WHERE did = ?", [activeDid] ); + + if (!accountExists?.values?.length) { + result.errors.push(`ActiveDid ${activeDid} not found in accounts table - data corruption detected`); + return result; + } + + // 3. Check if active_identity table already has data + const existingActiveIdentity = await dbQuery( + "SELECT activeDid FROM active_identity WHERE id = 1" + ); + + if (existingActiveIdentity?.values?.length) { + // Update existing record + await dbExec( + "UPDATE active_identity SET activeDid = ?, lastUpdated = datetime('now') WHERE id = 1", + [activeDid] + ); + } else { + // Insert new record + await dbExec( + "INSERT INTO active_identity (id, activeDid, lastUpdated) VALUES (1, ?, datetime('now'))", + [activeDid] + ); + } + + result.success = true; + result.dataMigrated = 1; + result.warnings.push(`Successfully migrated activeDid: ${activeDid}`); + + } catch (error) { + result.errors.push(`Migration failed: ${error}`); + logger.error("[ActiveDid Migration] Critical error during migration:", error); } + + return result; +} + +// Migration result interface +interface MigrationResult { + success: boolean; + errors: string[]; + warnings: string[]; + dataMigrated: number; } ``` -### Phase 3: API Updates +### Phase 3: Focused API Updates ```typescript -// Updated PlatformServiceMixin method +// Updated PlatformServiceMixin method - maintains backward compatibility async $accountSettings(did?: string, defaults: Settings = {}): Promise { - // Get settings without activeDid - const settings = await this._getSettingsWithoutActiveDid(); + try { + // Get settings without activeDid (unchanged logic) + const settings = await this._getSettingsWithoutActiveDid(); + + if (!settings) { + return defaults; + } + + // Get activeDid from new table (new logic) + const activeIdentity = await this._getActiveIdentity(); - // Get activeDid from separate table - const activeIdentity = await this._getActiveIdentity(); + // Return combined result (maintains backward compatibility) + return { ...settings, activeDid: activeIdentity.activeDid }; + } catch (error) { + logger.error("[Settings Trace] ❌ Error in $accountSettings:", error); + return defaults; + } +} - return { ...settings, activeDid: activeIdentity.activeDid }; +// New method for active identity management +async $getActiveIdentity(): Promise<{ activeDid: string | null }> { + try { + const result = await this.$dbQuery( + "SELECT activeDid FROM active_identity WHERE id = 1" + ); + + if (!result?.values?.length) { + return { activeDid: null }; + } + + return { activeDid: result.values[0][0] as string }; + } catch (error) { + logger.error("[Settings Trace] ❌ Failed to get active identity:", error); + return { activeDid: null }; + } } -// New method to get settings without activeDid +// Enhanced method to get settings without activeDid async _getSettingsWithoutActiveDid(): Promise { const result = await this.$dbQuery( "SELECT id, accountDid, apiServer, filterFeedByNearby, filterFeedByVisible, " + @@ -146,17 +240,80 @@ async _getSettingsWithoutActiveDid(): Promise { return this._mapColumnsToValues(result.columns, result.values)[0] as Settings; } -// New method to get active identity -async _getActiveIdentity(): Promise<{ activeDid: string | null }> { - const result = await this.$dbQuery( - "SELECT activeDid FROM active_identity WHERE id = 1" - ); +// Enhanced save settings method +async $saveSettings(changes: Partial): Promise { + try { + // Remove fields that shouldn't be updated + const { accountDid, id, activeDid, ...safeChanges } = changes; + + if (Object.keys(safeChanges).length > 0) { + // Convert settings for database storage + const convertedChanges = this._convertSettingsForStorage(safeChanges); + const setParts: string[] = []; + const params: unknown[] = []; + + Object.entries(convertedChanges).forEach(([key, value]) => { + if (value !== undefined) { + setParts.push(`${key} = ?`); + params.push(value); + } + }); - if (!result?.values?.length) { - return { activeDid: null }; + if (setParts.length > 0) { + params.push(MASTER_SETTINGS_KEY); + await this.$dbExec( + `UPDATE settings SET ${setParts.join(", ")} WHERE id = ?`, + params, + ); + } + } + + // Handle activeDid separately in new table + if (changes.activeDid !== undefined) { + await this.$updateActiveDid(changes.activeDid); + } + + return true; + } catch (error) { + logger.error("[PlatformServiceMixin] Error saving settings:", error); + return false; } +} - return { activeDid: result.values[0][0] as string }; +// Enhanced update activeDid method +async $updateActiveDid(newDid: string | null): Promise { + try { + if (newDid === null) { + // Clear active identity + await this.$dbExec( + "UPDATE active_identity SET activeDid = '', lastUpdated = datetime('now') WHERE id = 1" + ); + } else { + // Validate DID exists before setting + const accountExists = await this.$dbQuery( + "SELECT did FROM accounts WHERE did = ?", + [newDid] + ); + + if (!accountExists?.values?.length) { + logger.error(`[PlatformServiceMixin] Cannot set activeDid to non-existent DID: ${newDid}`); + return false; + } + + // Update active identity + await this.$dbExec( + "UPDATE active_identity SET activeDid = ?, lastUpdated = datetime('now') WHERE id = 1", + [newDid] + ); + } + + // Update internal tracking + await this._updateInternalActiveDid(newDid); + return true; + } catch (error) { + logger.error("[PlatformServiceMixin] Error updating activeDid:", error); + return false; + } } ``` @@ -165,15 +322,7 @@ async _getActiveIdentity(): Promise<{ activeDid: string | null }> { #### **1. Update `retrieveSettingsForDefaultAccount()`** ```typescript -// Current implementation in src/db/databaseUtil.ts:148 -export async function retrieveSettingsForDefaultAccount(): Promise { - const platform = PlatformServiceFactory.getInstance(); - const sql = "SELECT * FROM settings WHERE id = ?"; - const result = await platform.dbQuery(sql, [MASTER_SETTINGS_KEY]); - // ... rest of implementation -} - -// Updated implementation +// Enhanced implementation with active_identity table integration export async function retrieveSettingsForDefaultAccount(): Promise { const platform = PlatformServiceFactory.getInstance(); @@ -205,7 +354,24 @@ export async function retrieveSettingsForDefaultAccount(): Promise { ); if (activeIdentityResult?.values?.length) { - settings.activeDid = activeIdentityResult.values[0][0] as string; + const activeDid = activeIdentityResult.values[0][0] as string; + if (activeDid) { + // Validate activeDid exists in accounts + const accountExists = await platform.dbQuery( + "SELECT did FROM accounts WHERE did = ?", + [activeDid] + ); + + if (accountExists?.values?.length) { + settings.activeDid = activeDid; + } else { + logger.warn(`[databaseUtil] ActiveDid ${activeDid} not found in accounts, clearing`); + // Clear corrupted activeDid + await platform.dbExec( + "UPDATE active_identity SET activeDid = '', lastUpdated = datetime('now') WHERE id = 1" + ); + } + } } return settings; @@ -213,19 +379,10 @@ export async function retrieveSettingsForDefaultAccount(): Promise { } ``` - - #### **2. Update `$getMergedSettings()` Method** ```typescript -// Current implementation in PlatformServiceMixin.ts:485 -async $getMergedSettings(defaultKey: string, accountDid?: string, defaultFallback: Settings = {}): Promise { - // Get default settings - const defaultSettings = await this.$getSettings(defaultKey, defaultFallback); - // ... rest of implementation -} - -// Updated implementation +// Enhanced implementation with active_identity table integration async $getMergedSettings(defaultKey: string, accountDid?: string, defaultFallback: Settings = {}): Promise { try { // Get default settings (now without activeDid) @@ -240,7 +397,24 @@ async $getMergedSettings(defaultKey: string, accountDid?: string, defaultFallbac ); if (activeIdentityResult?.values?.length) { - defaultSettings.activeDid = activeIdentityResult.values[0][0] as string; + const activeDid = activeIdentityResult.values[0][0] as string; + if (activeDid) { + // Validate activeDid exists in accounts + const accountExists = await this.$dbQuery( + "SELECT did FROM accounts WHERE did = ?", + [activeDid] + ); + + if (accountExists?.values?.length) { + defaultSettings.activeDid = activeDid; + } else { + logger.warn(`[Settings Trace] ActiveDid ${activeDid} not found in accounts, clearing`); + // Clear corrupted activeDid + await this.$dbExec( + "UPDATE active_identity SET activeDid = '', lastUpdated = datetime('now') WHERE id = 1" + ); + } + } } } return defaultSettings || defaultFallback; @@ -257,58 +431,103 @@ async $getMergedSettings(defaultKey: string, accountDid?: string, defaultFallbac ## What Works (Evidence) - ✅ **Current activeDid storage** in settings table - - **Time**: 2025-01-27T18:30Z + - **Time**: 2025-08-29T07:24Z - **Evidence**: `src/db/tables/settings.ts:25` - activeDid field exists - **Verify at**: Current database schema and Settings type definition - ✅ **PlatformServiceMixin integration** with activeDid - - **Time**: 2025-01-27T18:30Z + - **Time**: 2025-08-29T07:24Z - **Evidence**: `src/utils/PlatformServiceMixin.ts:108` - activeDid tracking - **Verify at**: Component usage across all platforms - ✅ **Database migration infrastructure** exists - - **Time**: 2025-01-27T18:30Z + - **Time**: 2025-08-29T07:24Z - **Evidence**: `src/db-sql/migration.ts:31` - migration system in place - **Verify at**: Existing migration scripts and database versioning -- ✅ **Master settings functions architecture** supports migration - - **Time**: 2025-01-27T18:30Z - - **Evidence**: Functions use explicit field selection, not `SELECT *` - - **Verify at**: `src/db/databaseUtil.ts:148` and `src/utils/PlatformServiceMixin.ts:442` - ## What Doesn't (Evidence & Hypotheses) - ❌ **No separate active_identity table** exists - - **Time**: 2025-01-27T18:30Z + - **Time**: 2025-08-29T07:24Z - **Evidence**: Database schema only shows settings table - **Hypothesis**: Table needs to be created as part of migration - **Next probe**: Create migration script for new table -- ❌ **Platform services hardcoded** to settings table - - **Time**: 2025-01-27T18:30Z - - **Evidence**: `src/services/platforms/*.ts` - direct settings table access - - **Hypothesis**: All platform services need updates - - **Next probe**: Audit all platform service files for activeDid usage +- ❌ **Data corruption issues** with orphaned activeDid references + - **Time**: 2025-08-29T07:24Z + - **Evidence**: `IdentitySwitcherView.vue:175` - `hasCorruptedIdentity` detection + - **Hypothesis**: Current schema allows activeDid to point to non-existent accounts + - **Next probe**: Implement foreign key constraints in new table ## Risks, Limits, Assumptions - **Data Loss Risk**: Migration failure could lose activeDid values -- **Breaking Changes**: API updates required across all platform services +- **Breaking Changes**: API updates required in PlatformServiceMixin - **Rollback Complexity**: Schema changes make rollback difficult - **Testing Overhead**: All platforms must be tested with new structure - **Performance Impact**: Additional table join for activeDid retrieval - **Migration Timing**: Must be coordinated with other database changes +- **Data Corruption**: Current system has documented corruption issues +- **Foreign Key Constraints**: New constraints may prevent some operations + +## Enhanced Rollback Strategy + +### **Schema Rollback** +```sql +-- If migration fails, restore original schema +DROP TABLE IF EXISTS active_identity; + +-- Restore activeDid field to settings table if needed +ALTER TABLE settings ADD COLUMN activeDid TEXT; +``` + +### **Data Rollback** +```typescript +// Rollback function to restore activeDid to settings table +async function rollbackActiveDidMigration(): Promise { + try { + // Get activeDid from active_identity table + const activeIdentityResult = await dbQuery( + "SELECT activeDid FROM active_identity WHERE id = 1" + ); + + if (activeIdentityResult?.values?.length) { + const activeDid = activeIdentityResult.values[0][0] as string; + + // Restore to settings table + await dbExec( + "UPDATE settings SET activeDid = ? WHERE id = ?", + [activeDid, MASTER_SETTINGS_KEY] + ); + + return true; + } + + return false; + } catch (error) { + logger.error("[Rollback] Failed to restore activeDid:", error); + return false; + } +} +``` + +### **Rollback Triggers** +- Migration validation fails +- Data integrity checks fail +- Performance regression detected +- User reports data loss +- Cross-platform inconsistencies found ## Next Steps | Owner | Task | Exit Criteria | Target Date (UTC) | |-------|------|---------------|-------------------| -| Development Team | Create migration script | Migration script tested and validated | 2025-01-28 | -| Development Team | Update type definitions | Settings type updated, ActiveIdentity type created | 2025-01-28 | -| Development Team | Update platform services | All services use new active_identity table | 2025-01-29 | -| Development Team | Update PlatformServiceMixin | Mixin methods updated and tested | 2025-01-29 | -| QA Team | Platform testing | All platforms tested and verified | 2025-01-30 | -| Development Team | Deploy migration | Production deployment successful | 2025-01-31 | +| Development Team | Create enhanced migration script | Migration script with validation and rollback | 2025-08-30 | +| Development Team | Update type definitions | Settings type updated, ActiveIdentity type created | 2025-08-30 | +| Development Team | Update PlatformServiceMixin | Core methods updated and tested | 2025-08-31 | +| Development Team | Implement foreign key constraints | Schema validation prevents corruption | 2025-08-31 | +| QA Team | Platform testing | All platforms tested and verified | 2025-09-01 | +| Development Team | Deploy migration | Production deployment successful | 2025-09-02 | ## References @@ -320,21 +539,23 @@ async $getMergedSettings(defaultKey: string, accountDid?: string, defaultFallbac ## Competence Hooks - *Why this works*: Separates concerns between identity selection and - user preferences, improves database normalization, enables future - identity management features -- *Common pitfalls*: Forgetting to update all platform services, not - testing rollback scenarios, missing data validation during migration -- *Next skill unlock*: Advanced database schema design and migration - planning + user preferences, prevents data corruption with foreign key constraints, + centralizes identity management through API layer +- *Common pitfalls*: Forgetting to implement foreign key constraints, not + testing rollback scenarios, missing data validation during migration, + over-engineering component updates when API layer handles everything +- *Next skill unlock*: Advanced database schema design with constraints, + migration planning with rollback strategies - *Teach-back*: Explain the four-phase migration approach and why each - phase is necessary + phase is necessary, especially the foreign key constraints ## Collaboration Hooks - **Sign-off checklist**: - [ ] Migration script tested on development database - - [ ] All platform services updated and tested - - [ ] Rollback plan validated + - [ ] Foreign key constraints implemented and tested + - [ ] PlatformServiceMixin updated and tested + - [ ] Rollback procedures validated - [ ] Performance impact assessed - [ ] All stakeholders approve deployment timeline @@ -345,468 +566,40 @@ async $getMergedSettings(defaultKey: string, accountDid?: string, defaultFallbac - Migration can be completed without user downtime - Rollback to previous schema is acceptable if needed - Performance impact of additional table join is minimal - -## Component & View Impact Analysis - -### **High Impact Components** - -1. **`IdentitySection.vue`** - Receives `activeDid` as prop - - **Current**: Uses `activeDid` from parent component via prop - - **Impact**: **NO CHANGES REQUIRED** - Parent component handles migration - - **Risk**: **LOW** - No direct database access - - **Current Implementation:** - - ```vue - - - - ``` - - **Required Changes:** - - ```vue - - ``` - - **Key Insight**: This component requires **zero changes** since it receives - `activeDid` as a prop. The parent component that provides this prop will - handle the migration automatically through the API layer updates. - -2. **`DIDView.vue`** - Heavy activeDid usage - - **Current**: Initializes `activeDid` in `mounted()` lifecycle - - **Impact**: Must update initialization logic to use new table - - **Risk**: **HIGH** - Primary DID viewing component - - **Current Implementation:** - - ```vue - - ``` - - **Required Changes:** - - ```vue - - ``` - -3. **`HomeView.vue`** - ActiveDid change detection - - **Current**: Has `onActiveDidChanged()` watcher method - - **Impact**: Watcher logic needs updates for new data source - - **Risk**: **MEDIUM** - Core navigation component - - **Current Implementation:** - - ```vue - - ``` - - **Required Changes:** - - ```vue - - ``` - - **Key Insight**: HomeView will require minimal changes since it already uses - the `$accountSettings()` method, which will be updated to handle the new - table structure transparently. - -### **Medium Impact Components** - -1. **`InviteOneAcceptView.vue`** - Identity fallback logic - - **Current**: Creates identity if no `activeDid` exists - - **Impact**: Fallback logic needs to check new table - - **Risk**: **MEDIUM** - Invite processing component - - **Current Implementation:** - - ```vue - - ``` - - **Required Changes:** - - ```vue - - ``` - - **Key Insight**: This component will work automatically since it uses - `$accountSettings()`. The fallback logic doesn't need changes. - -2. **`ClaimView.vue`** - Settings retrieval - - **Current**: Gets `activeDid` from `$accountSettings()` - - **Impact**: Will automatically work if API is updated - - **Risk**: **LOW** - Depends on API layer updates - - **Current Implementation:** - - ```vue - - ``` - - **Required Changes:** - - ```vue - - ``` - - **Key Insight**: This component requires zero changes since it already - uses the proper API method. It's the lowest risk component. - -3. **`ContactAmountsView.vue`** - Uses phased-out method - - **Current**: Uses `$getSettings(MASTER_SETTINGS_KEY)` (being phased out) - - **Impact**: **NO CHANGES REQUIRED** - Will be updated when migrating to `getMasterSettings` - - **Risk**: **LOW** - Part of planned refactoring, not migration-specific - - **Note**: This component will be updated as part of the broader refactoring to - replace `$getSettings(MASTER_SETTINGS_KEY)` with `getMasterSettings()`, which - is separate from the activeDid migration. The migration plan focuses only on - components that require immediate changes for the active_identity table. - -### **Service Layer Impact** - -1. **`WebPlatformService.ts`** - - **Current**: Direct SQL queries to settings table - - **Impact**: Must add `active_identity` table queries - - **Risk**: **HIGH** - Core web platform service - -2. **`CapacitorPlatformService.ts`** - - **Current**: Similar direct SQL access - - **Impact**: Same updates as web service - - **Risk**: **HIGH** - Mobile platform service - -3. **`PlatformServiceMixin.ts`** - - **Current**: Core methods like `$accountSettings()`, `$saveSettings()` - - **Impact**: Major refactoring required - - **Risk**: **CRITICAL** - Used by 50+ components - -### **API Contract Changes** - -1. **`$saveSettings()` method** - - **Current**: Updates `settings.activeDid` - - **New**: Updates `active_identity.activeDid` - - **Impact**: All components using this method - -2. **`$updateActiveDid()` method** - - **Current**: Internal tracking only - - **New**: Database persistence required - - **Impact**: Identity switching logic - -### **Master Settings Functions Impact** - -1. **`retrieveSettingsForDefaultAccount()` function** - - **Current**: Returns settings with `activeDid` field from master settings - - **New**: Returns settings without `activeDid` field - - **Impact**: **HIGH** - Used by migration scripts and core database utilities - - **Location**: `src/db/databaseUtil.ts:148` - -2. **`$getMergedSettings()` method** - - **Current**: Merges default and account settings, includes `activeDid` from defaults - - **New**: Merges settings without `activeDid`, adds from `active_identity` table - - **Impact**: **HIGH** - Core method used by `$accountSettings()` - - **Location**: `src/utils/PlatformServiceMixin.ts:485` - -**Note**: `$getSettings(MASTER_SETTINGS_KEY)` is being phased out in favor of `getMasterSettings`, -so it doesn't require updates for this migration. - -### **New `getMasterSettings()` Function** - -Since we're phasing out `$getSettings(MASTER_SETTINGS_KEY)`, this migration -provides an opportunity to implement the new `getMasterSettings()` function -that will handle the active_identity table integration from the start: - -```typescript -// New getMasterSettings function to replace phased-out $getSettings -async getMasterSettings(): Promise { - try { - // Get master settings without activeDid - const result = await this.$dbQuery( - "SELECT id, accountDid, apiServer, filterFeedByNearby, filterFeedByVisible, " + - "finishedOnboarding, firstName, hideRegisterPromptOnNewContact, isRegistered, " + - "lastName, lastAckedOfferToUserJwtId, lastAckedOfferToUserProjectsJwtId, " + - "lastNotifiedClaimId, lastViewedClaimId, notifyingNewActivityTime, " + - "notifyingReminderMessage, notifyingReminderTime, partnerApiServer, " + - "passkeyExpirationMinutes, profileImageUrl, searchBoxes, showContactGivesInline, " + - "showGeneralAdvanced, showShortcutBvc, vapid, warnIfProdServer, warnIfTestServer, " + - "webPushServer FROM settings WHERE id = ?", - [MASTER_SETTINGS_KEY] - ); - - if (!result?.values?.length) { - return DEFAULT_SETTINGS; - } - - const settings = this._mapColumnsToValues(result.columns, result.values)[0] as Settings; - - // Handle JSON field parsing - if (settings.searchBoxes) { - settings.searchBoxes = this._parseJsonField(settings.searchBoxes, []); - } - - // Get activeDid from separate table - const activeIdentityResult = await this.$dbQuery( - "SELECT activeDid FROM active_identity WHERE id = 1" - ); - - if (activeIdentityResult?.values?.length) { - settings.activeDid = activeIdentityResult.values[0][0] as string; - } - - return settings; - } catch (error) { - logger.error(`[Settings Trace] ❌ Failed to get master settings:`, { error }); - return DEFAULT_SETTINGS; - } -} -``` - -### **Testing Impact** - -1. **Unit Tests** - - All platform service methods - - PlatformServiceMixin methods - - Database migration scripts - -2. **Integration Tests** - - Component behavior with new data source - - Identity switching workflows - - Settings persistence - -3. **Platform Tests** - - Web, Electron, iOS, Android - - Cross-platform data consistency - - Migration success on all platforms - -### **Performance Impact** - -1. **Additional Table Join** - - Settings queries now require active_identity table - - Potential performance impact on frequent operations - - Need for proper indexing - -2. **Caching Considerations** - - ActiveDid changes trigger cache invalidation - - Component re-rendering on identity switches - - Memory usage for additional table data - -### **Risk Assessment by Component Type** - -- **Critical Risk**: PlatformServiceMixin, Platform Services -- **High Risk**: Identity-related components, views using `$accountSettings()` -- **Medium Risk**: Components with direct settings access, identity management -- **Low Risk**: Components using only basic settings, utility components, -prop-based components, components using phased-out methods - -### **Migration Timeline Impact** - -- **Phase 1**: Schema Creation - No component impact -- **Phase 2**: Data Migration - No component impact -- **Phase 3**: API Updates - All components affected -- **Phase 4**: Cleanup - No component impact - -### **Update Priority Order** - -1. **PlatformServiceMixin** - Core dependency for most components -2. **Platform Services** - Ensure data access layer works -3. **Identity Components** - Verify core functionality -4. **Settings-Dependent Views** - Update in dependency order -5. **Utility Components** - Final cleanup and testing - -## Deferred for depth - -- Advanced identity management features enabled by this change -- Performance optimization strategies for the new table structure -- Future schema evolution planning -- Advanced rollback and recovery procedures +- Foreign key constraints will prevent future corruption +- API layer updates will handle component compatibility + +## What Needs to Change + +### **1. Database Schema** +- Create `active_identity` table with foreign key constraints +- Add performance indexes +- Remove `activeDid` field from `settings` table + +### **2. PlatformServiceMixin Methods** +- `$accountSettings()` - integrate with new table +- `$saveSettings()` - handle activeDid in new table +- `$updateActiveDid()` - validate and update new table +- `$getActiveIdentity()` - new method for identity management + +### **3. Master Settings Functions** +- `retrieveSettingsForDefaultAccount()` - integrate with new table +- `$getMergedSettings()` - integrate with new table + +### **4. Migration Scripts** +- Create migration script with validation +- Implement rollback procedures +- Add data corruption detection + +### **5. Type Definitions** +- Update Settings type to remove activeDid +- Create ActiveIdentity type for new table +- Update related interfaces + +## What Doesn't Need to Change + +- **All Vue components** - API layer handles migration transparently +- **Platform services** - Use PlatformServiceMixin, no direct access +- **User interface** - No changes to identity selection UI +- **Authentication flow** - Existing system unchanged +- **Component logic** - All activeDid handling through API methods