IndexedDB migration: set USE_DEXIE_DB to false, remove unused functions, add raw display of data

This commit is contained in:
2025-06-19 09:47:18 -06:00
parent 0f1810c967
commit a53f9dcbd6
4 changed files with 108 additions and 240 deletions

View File

@@ -2,7 +2,7 @@
<div class="min-h-screen bg-gray-50 py-8">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<!-- Header -->
<div class="mb-8">
<div>
<h1 class="text-3xl font-bold text-gray-900">Database Migration</h1>
<p class="mt-2 text-gray-600">
Compare and migrate data between Dexie (IndexedDB) and SQLite
@@ -10,33 +10,34 @@
</p>
</div>
<!-- Status Banner -->
<div
v-if="!isDexieEnabled"
class="mb-6 bg-yellow-50 border border-yellow-200 rounded-lg p-4"
>
<div class="flex">
<div class="flex-shrink-0">
<IconRenderer
icon-name="warning"
svg-class="h-5 w-5 text-yellow-400"
fill="currentColor"
/>
</div>
<div class="ml-3">
<h3 class="text-sm font-medium text-yellow-800">
Dexie Database Disabled
<div class="mt-4">
<!-- Migration Options -->
<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">
Migration Options
</h3>
<div class="mt-2 text-sm text-yellow-700">
<p>
To use migration features, enable Dexie database by setting
<code class="bg-yellow-100 px-1 rounded">
USE_DEXIE_DB = true
</code>
in
<code class="bg-yellow-100 px-1 rounded">
constants/app.ts
</code>
<div class="space-y-4">
<div class="flex items-center">
<input
id="overwrite-existing"
v-model="overwriteExisting"
type="checkbox"
class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"
/>
<label
for="overwrite-existing"
class="ml-2 block text-sm text-gray-900"
>
Overwrite existing records in SQLite
</label>
</div>
<p class="text-sm text-gray-600">
When enabled, existing records in SQLite will be updated with
data from Dexie. When disabled, existing records will be skipped
during migration.
</p>
</div>
</div>
@@ -44,9 +45,28 @@
</div>
<!-- Action Buttons -->
<div class="mb-8 flex flex-wrap gap-4">
<div class="mt-4 mb-8 flex flex-wrap gap-4">
<button
:disabled="isLoading || !isDexieEnabled"
:disabled="isLoading"
class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed"
@click="displayDatabases"
>
<IconRenderer
v-if="isLoading"
icon-name="spinner"
svg-class="animate-spin -ml-1 mr-3 h-5 w-5 text-white"
fill="currentColor"
/>
<IconRenderer
v-else
icon-name="chart"
svg-class="-ml-1 mr-3 h-5 w-5"
/>
Show Existing Data
</button>
<button
:disabled="isLoading"
class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed"
@click="compareDatabases"
>
@@ -65,7 +85,7 @@
</button>
<button
:disabled="isLoading || !isDexieEnabled || !comparison"
:disabled="isLoading || !comparison"
class="inline-flex items-center px-6 py-3 border border-transparent text-base font-medium rounded-md shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 disabled:opacity-50 disabled:cursor-not-allowed"
@click="migrateAll"
>
@@ -86,7 +106,7 @@
<div class="w-full border-t border-gray-200 my-4"></div>
<button
:disabled="isLoading || !isDexieEnabled || !comparison"
:disabled="isLoading || !comparison"
class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-green-600 hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 disabled:opacity-50 disabled:cursor-not-allowed"
@click="migrateContacts"
>
@@ -95,7 +115,7 @@
</button>
<button
:disabled="isLoading || !isDexieEnabled || !comparison"
:disabled="isLoading || !comparison"
class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-purple-600 hover:bg-purple-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-purple-500 disabled:opacity-50 disabled:cursor-not-allowed"
@click="migrateSettings"
>
@@ -104,7 +124,7 @@
</button>
<button
:disabled="isLoading || !isDexieEnabled || !comparison"
:disabled="isLoading || !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"
>
@@ -112,15 +132,6 @@
Migrate Accounts
</button>
<button
:disabled="isLoading || !isDexieEnabled"
class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-pink-600 hover:bg-pink-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-pink-500 disabled:opacity-50 disabled:cursor-not-allowed"
@click="testSpecificSettingsMigration"
>
<IconRenderer icon-name="test" svg-class="-ml-1 mr-3 h-5 w-5" />
Test Settings Migration
</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"
@@ -129,15 +140,6 @@
<IconRenderer icon-name="download" svg-class="-ml-1 mr-3 h-5 w-5" />
Export Comparison
</button>
<button
:disabled="isLoading"
class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-teal-600 hover:bg-teal-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-teal-500 disabled:opacity-50 disabled:cursor-not-allowed"
@click="verifyMigration"
>
<IconRenderer icon-name="check" svg-class="-ml-1 mr-3 h-5 w-5" />
Verify Migration
</button>
</div>
<!-- Migration Information -->
@@ -760,45 +762,27 @@
</div>
</div>
</div>
<!-- Migration Options -->
<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">
Migration Options
</h3>
<div class="space-y-4">
<div class="flex items-center">
<input
id="overwrite-existing"
v-model="overwriteExisting"
type="checkbox"
class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"
/>
<label
for="overwrite-existing"
class="ml-2 block text-sm text-gray-900"
>
Overwrite existing records in SQLite
</label>
</div>
<p class="text-sm text-gray-600">
When enabled, existing records in SQLite will be updated with
data from Dexie. When disabled, existing records will be skipped
during migration.
</p>
</div>
</div>
</div>
</div>
</div>
<!-- Export Results -->
<div v-if="exportedData" class="mt-4 space-y-6">
<h2>Exported Data</h2>
<span
v-on:click="copyToClipboard"
class="text-blue-500 cursor-pointer hover:text-blue-700"
>
Copy to Clipboard
</span>
<pre>{{ JSON.stringify(exportedData, null, 2) }}</pre>
</div>
</div>
</template>
<script lang="ts">
import { Component, Vue } from "vue-facing-decorator";
import { useClipboard } from "@vueuse/core";
import IconRenderer from "../components/IconRenderer.vue";
import {
compareDatabases,
@@ -807,11 +791,12 @@ import {
migrateAccounts,
migrateAll,
generateComparisonYaml,
testSettingsMigration,
type DataComparison,
type MigrationResult,
getDexieAccounts,
getDexieSettings,
getDexieContacts,
} from "../services/migrationService";
import { USE_DEXIE_DB } from "../constants/app";
import { logger } from "../utils/logger";
/**
@@ -844,18 +829,12 @@ export default class DatabaseMigration extends Vue {
private isLoading = false;
private loadingMessage = "";
private error = "";
private exportedData: Record<string, any> | null = null;
private successMessage = "";
private comparison: DataComparison | null = null;
private overwriteExisting = false;
private overwriteExisting = true;
/**
* Computed property to check if Dexie database is enabled
*
* @returns {boolean} True if Dexie database is enabled, false otherwise
*/
get isDexieEnabled(): boolean {
return USE_DEXIE_DB;
}
useClipboard = useClipboard;
/**
* Computed property to get the display name for a setting
@@ -927,6 +906,34 @@ export default class DatabaseMigration extends Vue {
return !!account.mnemonic;
}
/**
* Copies exported data to clipboard and shows success message
*/
async copyToClipboard(): Promise<void> {
if (!this.exportedData) return;
try {
await this.useClipboard().copy(JSON.stringify(this.exportedData, null, 2));
// Use global window object properly
if (typeof window !== 'undefined') {
window.alert('Copied to clipboard!');
}
} catch (error) {
console.error('Failed to copy to clipboard:', error);
if (typeof window !== 'undefined') {
window.alert('Failed to copy to clipboard');
}
}
}
async displayDatabases() {
this.exportedData = {
accounts: await getDexieAccounts(),
settings: await getDexieSettings(),
contacts: await getDexieContacts()
};
}
/**
* Migrates all data from Dexie to SQLite in the proper order
*
@@ -1147,47 +1154,6 @@ export default class DatabaseMigration extends Vue {
}
}
/**
* Verifies the migration by running a fresh comparison
*
* This method runs a new comparison after migration to verify
* that the data was transferred correctly.
*
* @async
* @returns {Promise<void>}
*/
async verifyMigration(): Promise<void> {
this.setLoading("Verifying migration...");
this.clearMessages();
try {
const newComparison = await compareDatabases();
const totalRemaining =
newComparison.differences.contacts.added.length +
newComparison.differences.settings.added.length +
newComparison.differences.accounts.added.length;
if (totalRemaining === 0) {
this.successMessage = "Migration verification successful! All data has been migrated.";
this.comparison = newComparison;
} else {
this.error = `Migration verification failed. ${totalRemaining} items still need to be migrated.`;
this.comparison = newComparison;
}
logger.info("[DatabaseMigration] Migration verification completed", {
totalRemaining,
success: totalRemaining === 0
});
} catch (error) {
this.error = `Failed to verify migration: ${error}`;
logger.error("[DatabaseMigration] Migration verification failed:", error);
} finally {
this.setLoading("");
}
}
/**
* Exports the comparison data to a JSON file
*
@@ -1224,34 +1190,6 @@ export default class DatabaseMigration extends Vue {
}
}
/**
* Tests the specific settings migration for the fields you mentioned
*
* This method tests the migration of firstName, isRegistered, profileImageUrl,
* showShortcutBvc, and searchBoxes from Dexie to SQLite.
*
* @async
* @returns {Promise<void>}
*/
async testSpecificSettingsMigration(): Promise<void> {
this.setLoading("Testing specific settings migration...");
this.clearMessages();
try {
await testSettingsMigration();
this.successMessage = "✅ Settings migration test completed successfully! Check the console for detailed logs.";
logger.info("[DatabaseMigration] Settings migration test completed successfully");
// Refresh comparison data after successful test
this.comparison = await compareDatabases();
} catch (error) {
this.error = `Settings migration test failed: ${error}`;
logger.error("[DatabaseMigration] Settings migration test failed:", error);
} finally {
this.setLoading("");
}
}
/**
* Sets the loading state and message
*