forked from trent_larson/crowd-funder-for-time-pwa
IndexedDB migration: set USE_DEXIE_DB to false, remove unused functions, add raw display of data
This commit is contained in:
@@ -66,7 +66,7 @@ Install dependencies:
|
|||||||
|
|
||||||
* Put the commit hash in the changelog (which will help you remember to bump the version later).
|
* Put the commit hash in the changelog (which will help you remember to bump the version later).
|
||||||
|
|
||||||
* Tag with the new version, [online](https://gitea.anomalistdesign.com/trent_larson/crowd-funder-for-time-pwa/releases) or `git tag 0.3.55 && git push origin 0.3.55`.
|
* Tag with the new version, [online](https://gitea.anomalistdesign.com/trent_larson/crowd-funder-for-time-pwa/releases) or `git tag 0.5.8 && git push origin 0.5.8`.
|
||||||
|
|
||||||
* For test, build the app (because test server is not yet set up to build):
|
* For test, build the app (because test server is not yet set up to build):
|
||||||
|
|
||||||
@@ -90,9 +90,9 @@ TIME_SAFARI_APP_TITLE="TimeSafari_Test" VITE_APP_SERVER=https://test.timesafari.
|
|||||||
|
|
||||||
* `pkgx +npm sh`
|
* `pkgx +npm sh`
|
||||||
|
|
||||||
* `cd crowd-funder-for-time-pwa && git checkout master && git pull && git checkout 0.3.55 && npm install && npm run build && cd -`
|
* `cd crowd-funder-for-time-pwa && git checkout master && git pull && git checkout 0.5.8 && npm install && npm run build:web && cd -`
|
||||||
|
|
||||||
(The plain `npm run build` uses the .env.production file.)
|
(The plain `npm run build:web` uses the .env.production file.)
|
||||||
|
|
||||||
* Back up the time-safari/dist folder & deploy: `mv time-safari/dist time-safari-dist-prev.0 && mv crowd-funder-for-time-pwa/dist time-safari/`
|
* Back up the time-safari/dist folder & deploy: `mv time-safari/dist time-safari-dist-prev.0 && mv crowd-funder-for-time-pwa/dist time-safari/`
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ export const IMAGE_TYPE_PROFILE = "profile";
|
|||||||
export const PASSKEYS_ENABLED =
|
export const PASSKEYS_ENABLED =
|
||||||
!!import.meta.env.VITE_PASSKEYS_ENABLED || false;
|
!!import.meta.env.VITE_PASSKEYS_ENABLED || false;
|
||||||
|
|
||||||
export const USE_DEXIE_DB = true;
|
export const USE_DEXIE_DB = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The possible values for "group" and "type" are in App.vue.
|
* The possible values for "group" and "type" are in App.vue.
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import { Settings, MASTER_SETTINGS_KEY } from "../db/tables/settings";
|
|||||||
import { Account } from "../db/tables/accounts";
|
import { Account } from "../db/tables/accounts";
|
||||||
import { logger } from "../utils/logger";
|
import { logger } from "../utils/logger";
|
||||||
import { parseJsonField } from "../db/databaseUtil";
|
import { parseJsonField } from "../db/databaseUtil";
|
||||||
import { USE_DEXIE_DB } from "../constants/app";
|
|
||||||
import { importFromMnemonic } from "../libs/util";
|
import { importFromMnemonic } from "../libs/util";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -133,10 +132,6 @@ export interface MigrationResult {
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
export async function getDexieContacts(): Promise<Contact[]> {
|
export async function getDexieContacts(): Promise<Contact[]> {
|
||||||
if (!USE_DEXIE_DB) {
|
|
||||||
throw new Error("Dexie database is not enabled");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await db.open();
|
await db.open();
|
||||||
const contacts = await db.contacts.toArray();
|
const contacts = await db.contacts.toArray();
|
||||||
@@ -215,8 +210,8 @@ export async function getSqliteContacts(): Promise<Contact[]> {
|
|||||||
* Retrieves all settings from the Dexie (IndexedDB) database
|
* Retrieves all settings from the Dexie (IndexedDB) database
|
||||||
*
|
*
|
||||||
* This function connects to the Dexie database and retrieves all settings
|
* This function connects to the Dexie database and retrieves all settings
|
||||||
* records. It requires that USE_DEXIE_DB is enabled in the app constants.
|
* records.
|
||||||
*
|
*
|
||||||
* Settings include both master settings (id=1) and account-specific settings
|
* Settings include both master settings (id=1) and account-specific settings
|
||||||
* that override the master settings for particular user accounts.
|
* that override the master settings for particular user accounts.
|
||||||
*
|
*
|
||||||
@@ -235,10 +230,6 @@ export async function getSqliteContacts(): Promise<Contact[]> {
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
export async function getDexieSettings(): Promise<Settings[]> {
|
export async function getDexieSettings(): Promise<Settings[]> {
|
||||||
if (!USE_DEXIE_DB) {
|
|
||||||
throw new Error("Dexie database is not enabled");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await db.open();
|
await db.open();
|
||||||
const settings = await db.settings.toArray();
|
const settings = await db.settings.toArray();
|
||||||
@@ -388,7 +379,7 @@ export async function getSqliteAccounts(): Promise<Account[]> {
|
|||||||
* Retrieves all accounts from the Dexie (IndexedDB) database
|
* Retrieves all accounts from the Dexie (IndexedDB) database
|
||||||
*
|
*
|
||||||
* This function connects to the Dexie database and retrieves all account
|
* This function connects to the Dexie database and retrieves all account
|
||||||
* records. It requires that USE_DEXIE_DB is enabled in the app constants.
|
* records.
|
||||||
*
|
*
|
||||||
* The function handles database opening and error conditions, providing
|
* The function handles database opening and error conditions, providing
|
||||||
* detailed logging for debugging purposes.
|
* detailed logging for debugging purposes.
|
||||||
@@ -408,10 +399,6 @@ export async function getSqliteAccounts(): Promise<Account[]> {
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
export async function getDexieAccounts(): Promise<Account[]> {
|
export async function getDexieAccounts(): Promise<Account[]> {
|
||||||
if (!USE_DEXIE_DB) {
|
|
||||||
throw new Error("Dexie database is not enabled");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const accountsDB = await accountsDBPromise;
|
const accountsDB = await accountsDBPromise;
|
||||||
await accountsDB.open();
|
await accountsDB.open();
|
||||||
@@ -1799,60 +1786,3 @@ export async function migrateAll(
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Test function to verify migration of specific settings fields
|
|
||||||
*
|
|
||||||
* This function tests the migration of the specific fields you mentioned:
|
|
||||||
* firstName, isRegistered, profileImageUrl, showShortcutBvc, and searchBoxes
|
|
||||||
*
|
|
||||||
* @returns Promise<void>
|
|
||||||
*/
|
|
||||||
export async function testSettingsMigration(): Promise<void> {
|
|
||||||
logger.info("[MigrationService] Starting settings migration test");
|
|
||||||
|
|
||||||
try {
|
|
||||||
// First, compare databases to see current state
|
|
||||||
const comparison = await compareDatabases();
|
|
||||||
logger.info("[MigrationService] Pre-migration comparison:", {
|
|
||||||
dexieSettings: comparison.dexieSettings.length,
|
|
||||||
sqliteSettings: comparison.sqliteSettings.length,
|
|
||||||
dexieAccounts: comparison.dexieAccounts.length,
|
|
||||||
sqliteAccounts: comparison.sqliteAccounts.length
|
|
||||||
});
|
|
||||||
|
|
||||||
// Run settings migration
|
|
||||||
const settingsResult = await migrateSettings(true);
|
|
||||||
logger.info("[MigrationService] Settings migration result:", settingsResult);
|
|
||||||
|
|
||||||
// Run accounts migration
|
|
||||||
const accountsResult = await migrateAccounts(true);
|
|
||||||
logger.info("[MigrationService] Accounts migration result:", accountsResult);
|
|
||||||
|
|
||||||
// Compare databases again to see changes
|
|
||||||
const postComparison = await compareDatabases();
|
|
||||||
logger.info("[MigrationService] Post-migration comparison:", {
|
|
||||||
dexieSettings: postComparison.dexieSettings.length,
|
|
||||||
sqliteSettings: postComparison.sqliteSettings.length,
|
|
||||||
dexieAccounts: postComparison.dexieAccounts.length,
|
|
||||||
sqliteAccounts: postComparison.sqliteAccounts.length
|
|
||||||
});
|
|
||||||
|
|
||||||
// Check if the specific fields were migrated
|
|
||||||
if (postComparison.sqliteSettings.length > 0) {
|
|
||||||
const sqliteSettings = postComparison.sqliteSettings[0];
|
|
||||||
logger.info("[MigrationService] Migrated settings fields:", {
|
|
||||||
firstName: sqliteSettings.firstName,
|
|
||||||
isRegistered: sqliteSettings.isRegistered,
|
|
||||||
profileImageUrl: sqliteSettings.profileImageUrl,
|
|
||||||
showShortcutBvc: sqliteSettings.showShortcutBvc,
|
|
||||||
searchBoxes: sqliteSettings.searchBoxes
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info("[MigrationService] Migration test completed successfully");
|
|
||||||
} catch (error) {
|
|
||||||
logger.error("[MigrationService] Migration test failed:", error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<div class="min-h-screen bg-gray-50 py-8">
|
<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">
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<div class="mb-8">
|
<div>
|
||||||
<h1 class="text-3xl font-bold text-gray-900">Database Migration</h1>
|
<h1 class="text-3xl font-bold text-gray-900">Database Migration</h1>
|
||||||
<p class="mt-2 text-gray-600">
|
<p class="mt-2 text-gray-600">
|
||||||
Compare and migrate data between Dexie (IndexedDB) and SQLite
|
Compare and migrate data between Dexie (IndexedDB) and SQLite
|
||||||
@@ -10,33 +10,34 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Status Banner -->
|
<div class="mt-4">
|
||||||
<div
|
<!-- Migration Options -->
|
||||||
v-if="!isDexieEnabled"
|
<div class="bg-white shadow rounded-lg">
|
||||||
class="mb-6 bg-yellow-50 border border-yellow-200 rounded-lg p-4"
|
<div class="px-4 py-5 sm:p-6">
|
||||||
>
|
<h3 class="text-lg leading-6 font-medium text-gray-900 mb-4">
|
||||||
<div class="flex">
|
Migration Options
|
||||||
<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
|
|
||||||
</h3>
|
</h3>
|
||||||
<div class="mt-2 text-sm text-yellow-700">
|
|
||||||
<p>
|
<div class="space-y-4">
|
||||||
To use migration features, enable Dexie database by setting
|
<div class="flex items-center">
|
||||||
<code class="bg-yellow-100 px-1 rounded">
|
<input
|
||||||
USE_DEXIE_DB = true
|
id="overwrite-existing"
|
||||||
</code>
|
v-model="overwriteExisting"
|
||||||
in
|
type="checkbox"
|
||||||
<code class="bg-yellow-100 px-1 rounded">
|
class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"
|
||||||
constants/app.ts
|
/>
|
||||||
</code>
|
<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>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -44,9 +45,28 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Action Buttons -->
|
<!-- Action Buttons -->
|
||||||
<div class="mb-8 flex flex-wrap gap-4">
|
<div class="mt-4 mb-8 flex flex-wrap gap-4">
|
||||||
<button
|
<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"
|
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"
|
@click="compareDatabases"
|
||||||
>
|
>
|
||||||
@@ -65,7 +85,7 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<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"
|
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"
|
@click="migrateAll"
|
||||||
>
|
>
|
||||||
@@ -86,7 +106,7 @@
|
|||||||
<div class="w-full border-t border-gray-200 my-4"></div>
|
<div class="w-full border-t border-gray-200 my-4"></div>
|
||||||
|
|
||||||
<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-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"
|
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"
|
@click="migrateContacts"
|
||||||
>
|
>
|
||||||
@@ -95,7 +115,7 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<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"
|
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"
|
@click="migrateSettings"
|
||||||
>
|
>
|
||||||
@@ -104,7 +124,7 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<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"
|
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"
|
@click="migrateAccounts"
|
||||||
>
|
>
|
||||||
@@ -112,15 +132,6 @@
|
|||||||
Migrate Accounts
|
Migrate Accounts
|
||||||
</button>
|
</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
|
<button
|
||||||
:disabled="!comparison"
|
: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"
|
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" />
|
<IconRenderer icon-name="download" svg-class="-ml-1 mr-3 h-5 w-5" />
|
||||||
Export Comparison
|
Export Comparison
|
||||||
</button>
|
</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>
|
</div>
|
||||||
|
|
||||||
<!-- Migration Information -->
|
<!-- Migration Information -->
|
||||||
@@ -760,45 +762,27 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</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>
|
||||||
</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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Component, Vue } from "vue-facing-decorator";
|
import { Component, Vue } from "vue-facing-decorator";
|
||||||
|
import { useClipboard } from "@vueuse/core";
|
||||||
|
|
||||||
import IconRenderer from "../components/IconRenderer.vue";
|
import IconRenderer from "../components/IconRenderer.vue";
|
||||||
import {
|
import {
|
||||||
compareDatabases,
|
compareDatabases,
|
||||||
@@ -807,11 +791,12 @@ import {
|
|||||||
migrateAccounts,
|
migrateAccounts,
|
||||||
migrateAll,
|
migrateAll,
|
||||||
generateComparisonYaml,
|
generateComparisonYaml,
|
||||||
testSettingsMigration,
|
|
||||||
type DataComparison,
|
type DataComparison,
|
||||||
type MigrationResult,
|
type MigrationResult,
|
||||||
|
getDexieAccounts,
|
||||||
|
getDexieSettings,
|
||||||
|
getDexieContacts,
|
||||||
} from "../services/migrationService";
|
} from "../services/migrationService";
|
||||||
import { USE_DEXIE_DB } from "../constants/app";
|
|
||||||
import { logger } from "../utils/logger";
|
import { logger } from "../utils/logger";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -844,18 +829,12 @@ export default class DatabaseMigration extends Vue {
|
|||||||
private isLoading = false;
|
private isLoading = false;
|
||||||
private loadingMessage = "";
|
private loadingMessage = "";
|
||||||
private error = "";
|
private error = "";
|
||||||
|
private exportedData: Record<string, any> | null = null;
|
||||||
private successMessage = "";
|
private successMessage = "";
|
||||||
private comparison: DataComparison | null = null;
|
private comparison: DataComparison | null = null;
|
||||||
private overwriteExisting = false;
|
private overwriteExisting = true;
|
||||||
|
|
||||||
/**
|
useClipboard = useClipboard;
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Computed property to get the display name for a setting
|
* Computed property to get the display name for a setting
|
||||||
@@ -927,6 +906,34 @@ export default class DatabaseMigration extends Vue {
|
|||||||
return !!account.mnemonic;
|
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
|
* 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
|
* 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
|
* Sets the loading state and message
|
||||||
*
|
*
|
||||||
|
|||||||
Reference in New Issue
Block a user