forked from jsnbuchanan/crowd-funder-for-time-pwa
feat: integrate importFromMnemonic utility into migration service and UI
- Add account migration support to migrationService with importFromMnemonic integration - Extend DataComparison and MigrationResult interfaces to include accounts - Add getDexieAccounts() and getSqliteAccounts() functions for account retrieval - Implement compareAccounts() and migrateAccounts() functions with proper error handling - Update DatabaseMigration.vue UI to support account migration: - Add "Migrate Accounts" button with lock icon - Extend summary cards grid to show Dexie/SQLite account counts - Add Account Differences section with added/modified/missing indicators - Update success message to include account migration counts - Enhance grid layouts to accommodate 6 summary cards and 3 difference sections The migration service now provides complete data migration capabilities for contacts, settings, and accounts, with enhanced reliability through the importFromMnemonic utility for mnemonic-based account handling.
This commit is contained in:
@@ -140,6 +140,27 @@
|
||||
Migrate Settings
|
||||
</button>
|
||||
|
||||
<button
|
||||
:disabled="isLoading || !isDexieEnabled || !comparison"
|
||||
class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-orange-600 hover:bg-orange-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-orange-500 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
@click="migrateAccounts"
|
||||
>
|
||||
<svg
|
||||
class="-ml-1 mr-3 h-5 w-5"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"
|
||||
/>
|
||||
</svg>
|
||||
Migrate Accounts
|
||||
</button>
|
||||
|
||||
<button
|
||||
:disabled="!comparison"
|
||||
class="inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
@@ -250,7 +271,7 @@
|
||||
<!-- Comparison Results -->
|
||||
<div v-if="comparison" class="space-y-6">
|
||||
<!-- Summary Cards -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-6 gap-6">
|
||||
<div class="bg-white overflow-hidden shadow rounded-lg">
|
||||
<div class="p-5">
|
||||
<div class="flex items-center">
|
||||
@@ -384,10 +405,74 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white overflow-hidden shadow rounded-lg">
|
||||
<div class="p-5">
|
||||
<div class="flex items-center">
|
||||
<div class="flex-shrink-0">
|
||||
<svg
|
||||
class="h-6 w-6 text-orange-600"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="ml-5 w-0 flex-1">
|
||||
<dl>
|
||||
<dt class="text-sm font-medium text-gray-500 truncate">
|
||||
Dexie Accounts
|
||||
</dt>
|
||||
<dd class="text-lg font-medium text-gray-900">
|
||||
{{ comparison.dexieAccounts.length }}
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white overflow-hidden shadow rounded-lg">
|
||||
<div class="p-5">
|
||||
<div class="flex items-center">
|
||||
<div class="flex-shrink-0">
|
||||
<svg
|
||||
class="h-6 w-6 text-teal-600"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="ml-5 w-0 flex-1">
|
||||
<dl>
|
||||
<dt class="text-sm font-medium text-gray-500 truncate">
|
||||
SQLite Accounts
|
||||
</dt>
|
||||
<dd class="text-lg font-medium text-gray-900">
|
||||
{{ comparison.sqliteAccounts.length }}
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Differences Section -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||
<!-- Contacts Differences -->
|
||||
<div class="bg-white shadow rounded-lg">
|
||||
<div class="px-4 py-5 sm:p-6">
|
||||
@@ -601,6 +686,112 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Account Differences -->
|
||||
<div class="bg-white shadow rounded-lg">
|
||||
<div class="px-4 py-5 sm:p-6">
|
||||
<h3 class="text-lg leading-6 font-medium text-gray-900 mb-4">
|
||||
Account Differences
|
||||
</h3>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div
|
||||
class="flex items-center justify-between p-3 bg-blue-50 rounded-lg"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
<svg
|
||||
class="h-5 w-5 text-blue-600 mr-2"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M12 6v6m0 0v6m0-6h6m-6 0H6"
|
||||
/>
|
||||
</svg>
|
||||
<span class="text-sm font-medium text-blue-900">Added</span>
|
||||
</div>
|
||||
<span class="text-sm font-bold text-blue-900">{{
|
||||
comparison.differences.accounts.added.length
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex items-center justify-between p-3 bg-yellow-50 rounded-lg"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
<svg
|
||||
class="h-5 w-5 text-yellow-600 mr-2"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"
|
||||
/>
|
||||
</svg>
|
||||
<span class="text-sm font-medium text-yellow-900"
|
||||
>Modified</span
|
||||
>
|
||||
</div>
|
||||
<span class="text-sm font-bold text-yellow-900">{{
|
||||
comparison.differences.accounts.modified.length
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex items-center justify-between p-3 bg-red-50 rounded-lg"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
<svg
|
||||
class="h-5 w-5 text-red-600 mr-2"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
|
||||
/>
|
||||
</svg>
|
||||
<span class="text-sm font-medium text-red-900"
|
||||
>Missing</span
|
||||
>
|
||||
</div>
|
||||
<span class="text-sm font-bold text-red-900">{{
|
||||
comparison.differences.accounts.missing.length
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Account Details -->
|
||||
<div
|
||||
v-if="comparison.differences.accounts.added.length > 0"
|
||||
class="mt-4"
|
||||
>
|
||||
<h4 class="text-sm font-medium text-gray-900 mb-2">
|
||||
Added Accounts:
|
||||
</h4>
|
||||
<div class="max-h-32 overflow-y-auto space-y-1">
|
||||
<div
|
||||
v-for="account in comparison.differences.accounts.added"
|
||||
:key="account.id"
|
||||
class="text-xs text-gray-600 bg-gray-50 p-2 rounded"
|
||||
>
|
||||
ID: {{ account.id }} - {{ account.did.substring(0, 20) }}...
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Migration Options -->
|
||||
@@ -645,6 +836,7 @@ import {
|
||||
compareDatabases,
|
||||
migrateContacts,
|
||||
migrateSettings,
|
||||
migrateAccounts,
|
||||
generateComparisonYaml,
|
||||
type DataComparison,
|
||||
type MigrationResult,
|
||||
@@ -707,7 +899,7 @@ export default class DatabaseMigration extends Vue {
|
||||
|
||||
try {
|
||||
this.comparison = await compareDatabases();
|
||||
this.successMessage = `Comparison completed successfully. Found ${this.comparison.differences.contacts.added.length + this.comparison.differences.settings.added.length} items to migrate.`;
|
||||
this.successMessage = `Comparison completed successfully. Found ${this.comparison.differences.contacts.added.length + this.comparison.differences.settings.added.length + this.comparison.differences.accounts.added.length} items to migrate.`;
|
||||
logger.info(
|
||||
"[DatabaseMigration] Database comparison completed successfully",
|
||||
);
|
||||
@@ -803,6 +995,49 @@ export default class DatabaseMigration extends Vue {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates accounts from Dexie to SQLite database
|
||||
*
|
||||
* This method transfers accounts from the Dexie database to SQLite,
|
||||
* with options to overwrite existing records. For accounts with mnemonic
|
||||
* data, it uses the importFromMnemonic utility for proper key derivation.
|
||||
*
|
||||
* @async
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async migrateAccounts(): Promise<void> {
|
||||
this.setLoading("Migrating accounts...");
|
||||
this.clearMessages();
|
||||
|
||||
try {
|
||||
const result: MigrationResult = await migrateAccounts(
|
||||
this.overwriteExisting,
|
||||
);
|
||||
|
||||
if (result.success) {
|
||||
this.successMessage = `Successfully migrated ${result.accountsMigrated} accounts.`;
|
||||
if (result.warnings.length > 0) {
|
||||
this.successMessage += ` ${result.warnings.length} warnings.`;
|
||||
}
|
||||
logger.info(
|
||||
"[DatabaseMigration] Account migration completed successfully",
|
||||
result,
|
||||
);
|
||||
} else {
|
||||
this.error = `Migration failed: ${result.errors.join(", ")}`;
|
||||
logger.error(
|
||||
"[DatabaseMigration] Account migration failed:",
|
||||
result.errors,
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
this.error = `Failed to migrate accounts: ${error}`;
|
||||
logger.error("[DatabaseMigration] Account migration failed:", error);
|
||||
} finally {
|
||||
this.setLoading("");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports comparison results to a file
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user