forked from trent_larson/crowd-funder-for-time-pwa
- Extract test user data (seed phrases, DIDs, usernames) from importUser into separate getTestUserData function
- Refactor importUser to use getTestUserData internally, maintaining backward compatibility
- Update "New offers for another user" test to use new getTestUserData function
- Replace hardcoded seed phrase with programmatic retrieval using getTestUserData('00')
- Add proper TypeScript type annotations to array functions in testUtils
- Improve test maintainability by centralizing test user data management
This allows tests to access user data without executing import flow, providing more flexibility for test scenarios.
236 lines
6.9 KiB
Vue
236 lines
6.9 KiB
Vue
<template>
|
|
<section id="Content" class="p-6 pb-24 max-w-3xl mx-auto">
|
|
<!-- Breadcrumb -->
|
|
<div id="ViewBreadcrumb" class="mb-8">
|
|
<h1 class="text-lg text-center font-light relative px-7">
|
|
<!-- Cancel -->
|
|
<button
|
|
class="text-lg text-center px-2 py-1 absolute -left-2 -top-1"
|
|
@click="$router.go(-1)"
|
|
>
|
|
<font-awesome icon="chevron-left"></font-awesome>
|
|
</button>
|
|
Import Existing Identifier
|
|
</h1>
|
|
</div>
|
|
<!-- Import Account Form -->
|
|
<p class="text-center text-xl mb-4 font-light">
|
|
Enter your seed phrase below to import your identifier on this device.
|
|
</p>
|
|
<!-- id used by puppeteer test script -->
|
|
<textarea
|
|
id="seed-input"
|
|
v-model="mnemonic"
|
|
type="text"
|
|
placeholder="Seed Phrase"
|
|
class="block w-full rounded border border-slate-400 mb-4 px-3 py-2"
|
|
/>
|
|
|
|
<h3
|
|
class="text-blue-500 text-sm font-semibold mb-3"
|
|
@click="showAdvanced = !showAdvanced"
|
|
>
|
|
Advanced
|
|
</h3>
|
|
<div v-if="showAdvanced">
|
|
Enter a custom derivation path
|
|
<input
|
|
v-model="derivationPath"
|
|
type="text"
|
|
class="block w-full rounded border border-slate-400 mb-2 px-3 py-2"
|
|
/>
|
|
<span class="ml-4">
|
|
For previous uPort or Endorser users,
|
|
<a
|
|
class="text-blue-500"
|
|
@click="derivationPath = UPORT_DERIVATION_PATH"
|
|
>
|
|
click here to use that value.
|
|
</a>
|
|
</span>
|
|
|
|
<div v-if="numAccounts == 1" class="mt-4">
|
|
<input v-model="shouldErase" type="checkbox" class="mr-2" />
|
|
<label>Erase previous identifiers.</label>
|
|
</div>
|
|
|
|
<div v-if="isNotProdServer()" class="mt-4 text-blue-500">
|
|
<!-- if they click this, fill in the mnemonic seed-input with the test mnemonic -->
|
|
<button @click="mnemonic = TEST_USER_0_MNEMONIC">
|
|
Use mnemonic for Test User #0
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-8">
|
|
<div class="grid grid-cols-1 sm:grid-cols-2 gap-2">
|
|
<button
|
|
class="block w-full text-center text-lg font-bold uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-3 rounded-md"
|
|
@click="onImportClick()"
|
|
>
|
|
Import
|
|
</button>
|
|
<button
|
|
type="button"
|
|
class="block w-full text-center text-md uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md"
|
|
@click="onCancelClick()"
|
|
>
|
|
Cancel
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import { Component, Vue } from "vue-facing-decorator";
|
|
import { Router } from "vue-router";
|
|
|
|
import { AppString, NotificationIface } from "../constants/app";
|
|
import { DEFAULT_ROOT_DERIVATION_PATH } from "../libs/crypto";
|
|
import { retrieveAccountCount, importFromMnemonic } from "../libs/util";
|
|
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
|
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
|
|
|
|
/**
|
|
* Import Account View Component
|
|
*
|
|
* Allows users to import existing identifiers using seed phrases:
|
|
* - Secure mnemonic phrase input with validation
|
|
* - Advanced options for custom derivation paths
|
|
* - Legacy uPort compatibility support
|
|
* - Test environment utilities for development
|
|
*
|
|
* Features:
|
|
* - Secure seed phrase import functionality
|
|
* - Custom derivation path configuration
|
|
* - Account erasure options for fresh imports
|
|
* - Development mode test utilities
|
|
* - Comprehensive error handling and validation
|
|
*
|
|
* Security Considerations:
|
|
* - Seed phrases are handled securely and not logged
|
|
* - Import process includes validation and error recovery
|
|
* - Advanced options are hidden by default
|
|
*
|
|
* @author Matthew Raymer
|
|
*/
|
|
@Component({
|
|
components: {},
|
|
mixins: [PlatformServiceMixin],
|
|
})
|
|
export default class ImportAccountView extends Vue {
|
|
TEST_USER_0_MNEMONIC =
|
|
"rigid shrug mobile smart veteran half all pond toilet brave review universe ship congress found yard skate elite apology jar uniform subway slender luggage";
|
|
UPORT_DERIVATION_PATH = "m/7696500'/0'/0'/0'"; // for legacy imports, likely never used
|
|
|
|
AppString = AppString;
|
|
|
|
$notify!: (notification: NotificationIface, timeout?: number) => void;
|
|
$router!: Router;
|
|
|
|
notify!: ReturnType<typeof createNotifyHelpers>;
|
|
|
|
apiServer = "";
|
|
derivationPath = DEFAULT_ROOT_DERIVATION_PATH;
|
|
mnemonic = "";
|
|
numAccounts = 0;
|
|
showAdvanced = false;
|
|
shouldErase = false;
|
|
|
|
/**
|
|
* Initializes notification helpers
|
|
*/
|
|
created() {
|
|
this.notify = createNotifyHelpers(this.$notify);
|
|
}
|
|
|
|
/**
|
|
* Component initialization
|
|
*
|
|
* Loads account count and server settings for import configuration
|
|
* Uses PlatformServiceMixin for secure database access
|
|
*/
|
|
async mounted() {
|
|
await this.initializeSettings();
|
|
}
|
|
|
|
/**
|
|
* Initializes component settings and account information
|
|
*/
|
|
private async initializeSettings() {
|
|
this.numAccounts = await retrieveAccountCount();
|
|
const settings = await this.$accountSettings();
|
|
this.apiServer = settings.apiServer || "";
|
|
}
|
|
|
|
/**
|
|
* Handles cancel button click
|
|
*
|
|
* Navigates back to previous view
|
|
*/
|
|
public onCancelClick() {
|
|
this.$router.back();
|
|
}
|
|
|
|
/**
|
|
* Checks if running on production server
|
|
*
|
|
* @returns True if not on production server (enables test utilities)
|
|
*/
|
|
public isNotProdServer() {
|
|
return this.apiServer !== AppString.PROD_ENDORSER_API_SERVER;
|
|
}
|
|
|
|
/**
|
|
* Handles import button click
|
|
*
|
|
* Validates input and initiates account import process
|
|
* Uses importFromMnemonic utility for secure import
|
|
*/
|
|
public async onImportClick() {
|
|
console.log("[ImportAccountView] onImportClick", this.mnemonic);
|
|
if (!this.mnemonic?.trim()) {
|
|
this.notify.warning(
|
|
"Seed phrase is required to import an account.",
|
|
TIMEOUTS.LONG,
|
|
);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
await importFromMnemonic(
|
|
this.mnemonic,
|
|
this.derivationPath,
|
|
this.shouldErase,
|
|
);
|
|
|
|
// Check what was actually imported
|
|
const settings = await this.$accountSettings();
|
|
console.log("[ImportAccountView] settings", JSON.stringify(settings, null, 2));
|
|
|
|
// Check account-specific settings
|
|
if (settings?.activeDid) {
|
|
try {
|
|
await this.$query("SELECT * FROM settings WHERE accountDid = ?", [
|
|
settings.activeDid,
|
|
]);
|
|
} catch (error) {
|
|
// Log error but don't interrupt import flow
|
|
this.$logError("Error checking post-import settings: " + error);
|
|
}
|
|
}
|
|
|
|
this.notify.success("Account imported successfully!", TIMEOUTS.STANDARD);
|
|
this.$router.push({ name: "account" });
|
|
} catch (error: any) {
|
|
this.$logError("Import failed: " + error);
|
|
this.notify.error(
|
|
error.message || "Failed to import account.",
|
|
TIMEOUTS.LONG,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
</script>
|