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
This commit is contained in:
@@ -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`:
|
||||||
|
|||||||
@@ -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
Normal file
14
src/db/tables/activeIdentity.ts
Normal file
@@ -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;
|
||||||
|
}
|
||||||
@@ -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
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user