Browse Source

feat(activeDid): implement migration to separate active_identity table

- Add migration 003 with data migration logic to prevent data loss
- Create dedicated ActiveIdentity interface in separate file for better architecture
- Implement $getActiveIdentity method in PlatformServiceMixin
- Enhance $updateActiveDid with dual-write pattern for backward compatibility
- Maintain separation of concerns between settings and active identity types
- Follow project architectural pattern with dedicated type definition files

The migration creates active_identity table alongside existing settings,
automatically copying existing activeDid data to prevent user data loss.
Dual-write pattern ensures backward compatibility during transition.

Migration includes:
- Schema creation with proper constraints and indexes
- Automatic data transfer from settings.activeDid to active_identity.activeDid
- Validation to ensure data exists before migration
- Atomic operation: schema and data migration happen together
pull/188/head
Matthew Raymer 3 weeks ago
parent
commit
4a22a35b3e
  1. 7
      doc/activeDid-migration-plan.md
  2. 27
      src/db-sql/migration.ts
  3. 14
      src/db/tables/activeIdentity.ts
  4. 35
      src/utils/PlatformServiceMixin.ts

7
doc/activeDid-migration-plan.md

@ -117,10 +117,10 @@ runs on existing databases.
### **2. Type Definitions** ### **2. Type Definitions**
Create ActiveIdentity interface in `src/db/tables/settings.ts`: Create ActiveIdentity interface in `src/db/tables/activeIdentity.ts`:
```typescript ```typescript
// Add to src/db/tables/settings.ts // Create new file: src/db/tables/activeIdentity.ts
export interface ActiveIdentity { export interface ActiveIdentity {
id: number; id: number;
activeDid: string; activeDid: string;
@ -128,6 +128,9 @@ export interface ActiveIdentity {
} }
``` ```
**Note**: This maintains separation of concerns by keeping active identity types
separate from settings types, following the project's architectural pattern.
### **3. PlatformServiceMixin Methods** ### **3. PlatformServiceMixin Methods**
Implement required methods in `src/utils/PlatformServiceMixin.ts`: Implement required methods in `src/utils/PlatformServiceMixin.ts`:

27
src/db-sql/migration.ts

@ -124,6 +124,33 @@ const MIGRATIONS = [
ALTER TABLE contacts ADD COLUMN iViewContent BOOLEAN DEFAULT TRUE; ALTER TABLE contacts ADD COLUMN iViewContent BOOLEAN DEFAULT TRUE;
`, `,
}, },
{
name: "003_active_did_separate_table",
sql: `
-- Create new active_identity table with proper constraints
CREATE TABLE IF NOT EXISTS active_identity (
id INTEGER PRIMARY KEY CHECK (id = 1),
activeDid TEXT NOT NULL,
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 OR IGNORE INTO active_identity (id, activeDid, lastUpdated) VALUES (1, '', datetime('now'));
-- MIGRATE EXISTING DATA: Copy activeDid from settings to active_identity
-- This prevents data loss when migration runs on existing databases
UPDATE active_identity
SET activeDid = (SELECT activeDid FROM settings WHERE id = 1),
lastUpdated = datetime('now')
WHERE id = 1
AND EXISTS (SELECT 1 FROM settings WHERE id = 1 AND activeDid IS NOT NULL AND activeDid != '');
`,
},
]; ];
/** /**

14
src/db/tables/activeIdentity.ts

@ -0,0 +1,14 @@
/**
* ActiveIdentity type describes the active identity selection.
* This replaces the activeDid field in the settings table for better
* database architecture and data integrity.
*
* @author Matthew Raymer
* @since 2025-08-29
*/
export interface ActiveIdentity {
id: number;
activeDid: string;
lastUpdated: string;
}

35
src/utils/PlatformServiceMixin.ts

@ -58,6 +58,7 @@ import {
generateInsertStatement, generateInsertStatement,
generateUpdateStatement, generateUpdateStatement,
} from "@/utils/sqlHelpers"; } from "@/utils/sqlHelpers";
import { ActiveIdentity } from "@/db/tables/activeIdentity";
// ================================================= // =================================================
// TYPESCRIPT INTERFACES // TYPESCRIPT INTERFACES
@ -548,6 +549,40 @@ export const PlatformServiceMixin = {
} }
}, },
/**
* Get active identity from the new active_identity table
* This replaces the activeDid field in settings for better architecture
*/
async $getActiveIdentity(): Promise<ActiveIdentity> {
try {
const result = await this.$dbQuery(
"SELECT id, activeDid, lastUpdated FROM active_identity WHERE id = 1",
);
if (result?.values?.length) {
const [id, activeDid, lastUpdated] = result.values[0];
return {
id: id as number,
activeDid: activeDid as string,
lastUpdated: lastUpdated as string,
};
}
// Return default if no record exists
return {
id: 1,
activeDid: "",
lastUpdated: new Date().toISOString(),
};
} catch (error) {
logger.error(
"[PlatformServiceMixin] Error getting active identity:",
error,
);
throw error;
}
},
/** /**
* Transaction wrapper with automatic rollback on error * Transaction wrapper with automatic rollback on error
*/ */

Loading…
Cancel
Save