Browse Source

Merge pull request 'separate account from other data for backup/restore' (#13) from separate-dbs into master

Reviewed-on: https://gitea.anomalistdesign.com/trent_larson/kick-starter-for-time-pwa/pulls/13
kb/add-usage-guide
anomalist 2 years ago
parent
commit
c47d6d8ae4
  1. 9
      README.md
  2. 9
      package-lock.json
  3. 1
      package.json
  4. 15
      project.yaml
  5. 41
      src/db/index.ts
  6. 26
      src/db/tables/accounts.ts
  7. 11
      src/db/tables/contacts.ts
  8. 13
      src/db/tables/settings.ts
  9. 12
      src/router/index.ts
  10. 6
      src/test/index.ts
  11. 93
      src/views/AccountViewView.vue
  12. 12
      src/views/ContactsView.vue
  13. 2
      src/views/DiscoverView.vue
  14. 139
      src/views/HelpView.vue
  15. 8
      src/views/ImportAccountView.vue
  16. 6
      src/views/NewEditAccountView.vue
  17. 29
      src/views/NewEditProjectView.vue
  18. 14
      src/views/ProjectViewView.vue
  19. 10
      src/views/ProjectsView.vue

9
README.md

@ -44,13 +44,14 @@ New users require registration. This can be done with a claim payload like this
On the test server, User #0 has rights to register others, so you can start playing one of two ways: On the test server, User #0 has rights to register others, so you can start playing one of two ways:
- Import the keys for that test User `did:ethr:0x000Ee5654b9742f6Fe18ea970e32b97ee2247B51` by importing this seed phrase: `seminar accuse mystery assist delay law thing deal image undo guard initial shallow wrestle list fragile borrow velvet tomorrow awake explain test offer control` - Import the keys for the test User `did:ethr:0x000Ee5654b9742f6Fe18ea970e32b97ee2247B51` by importing this seed phrase:
`seminar accuse mystery assist delay law thing deal image undo guard initial shallow wrestle list fragile borrow velvet tomorrow awake explain test offer control`
- Register someone else under User #0 on the `/account` page: - Alternatively, register someone else under User #0 on the `/account` page:
* Edit the `src/views/AccountViewView.vue` file and uncomment the lines referring to "test". * In the `src/views/AccountViewView.vue` file, uncomment the lines referring to "testServerRegisterUser".
* Use the [Vue Devtools browser extension](https://devtools.vuejs.org/) and type this into the console: `$vm.ctx.testRegisterUser()` * Visit the `/account` page.

9
package-lock.json

@ -26,6 +26,7 @@
"class-transformer": "^0.5.1", "class-transformer": "^0.5.1",
"core-js": "^3.26.1", "core-js": "^3.26.1",
"dexie": "^3.2.2", "dexie": "^3.2.2",
"dexie-export-import": "^4.0.6",
"did-jwt": "^6.9.0", "did-jwt": "^6.9.0",
"ethereum-cryptography": "^1.1.2", "ethereum-cryptography": "^1.1.2",
"ethereumjs-util": "^7.1.5", "ethereumjs-util": "^7.1.5",
@ -12187,6 +12188,14 @@
"node": ">=6.0" "node": ">=6.0"
} }
}, },
"node_modules/dexie-export-import": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/dexie-export-import/-/dexie-export-import-4.0.6.tgz",
"integrity": "sha512-WFTSm/XB4MNHBZVQJTrWjWvqur7vfpx1mWEWMuTUB9zdW3FTXvkpm7SvsUX55r6y6wmJ3libwA5eRCbVUKmuTw==",
"peerDependencies": {
"dexie": "^2.0.4 || ^3.0.0 || ^4.0.1-alpha.5"
}
},
"node_modules/did-jwt": { "node_modules/did-jwt": {
"version": "6.9.0", "version": "6.9.0",
"resolved": "https://registry.npmjs.org/did-jwt/-/did-jwt-6.9.0.tgz", "resolved": "https://registry.npmjs.org/did-jwt/-/did-jwt-6.9.0.tgz",

1
package.json

@ -26,6 +26,7 @@
"class-transformer": "^0.5.1", "class-transformer": "^0.5.1",
"core-js": "^3.26.1", "core-js": "^3.26.1",
"dexie": "^3.2.2", "dexie": "^3.2.2",
"dexie-export-import": "^4.0.6",
"did-jwt": "^6.9.0", "did-jwt": "^6.9.0",
"ethereum-cryptography": "^1.1.2", "ethereum-cryptography": "^1.1.2",
"ethereumjs-util": "^7.1.5", "ethereumjs-util": "^7.1.5",

15
project.yaml

@ -11,11 +11,24 @@
- replace user-affecting console.logs with error messages (eg. catches) - replace user-affecting console.logs with error messages (eg. catches)
- contacts v1: - contacts v1 :
- .2 show gives with new setting
- 01 show gives with confirmations
- .5 Add page to show seed.
- 01 Provide a way to import the non-sensitive data.
- 01 Provide way to share your contact info.
- .1 remove "scan new contact"
- contacts v+ :
- .5 make advanced "show/hide amounts" button into a nice UI toggle - .5 make advanced "show/hide amounts" button into a nice UI toggle
- .2 show error to user when adding a duplicate contact - .2 show error to user when adding a duplicate contact
- parse input more robustly (with CSV lib and not commas) - parse input more robustly (with CSV lib and not commas)
- refactor UI :
- .5 Alerts show at the top and can be missed, eg. account data download
- 01 Code for "nav" tabs across the bottom is duplicated on each page.
- .2 Add "copied" feedback when they click "copy" on /account
- commit screen - commit screen
- discover screen - discover screen

41
src/db/index.ts

@ -1,17 +1,19 @@
import BaseDexie, { Table } from "dexie"; import BaseDexie, { Table } from "dexie";
import { encrypted, Encryption } from "@pvermeer/dexie-encrypted-addon"; import { encrypted, Encryption } from "@pvermeer/dexie-encrypted-addon";
import { Account, AccountsSchema } from "./tables/accounts";
import { Contact, ContactsSchema } from "./tables/contacts";
import { import {
Account, MASTER_SETTINGS_KEY,
AccountsSchema,
Contact,
ContactsSchema,
MASTER_SETTINGS,
Settings, Settings,
SettingsSchema, SettingsSchema,
} from "./tables"; } from "./tables/settings";
type AllTables = { // a separate DB because the seed is super-sensitive data
type SensitiveTables = {
accounts: Table<Account>; accounts: Table<Account>;
};
type NonsensitiveTables = {
contacts: Table<Contact>; contacts: Table<Contact>;
settings: Table<Settings>; settings: Table<Settings>;
}; };
@ -24,15 +26,14 @@ type AllTables = {
* *
* https://9to5answer.com/how-to-bypass-warning-unexpected-any-specify-a-different-type-typescript-eslint-no-explicit-any * https://9to5answer.com/how-to-bypass-warning-unexpected-any-specify-a-different-type-typescript-eslint-no-explicit-any
*/ */
type DexieTables = AllTables; export type SensitiveDexie<T extends unknown = SensitiveTables> = BaseDexie & T;
export type Dexie<T extends unknown = DexieTables> = BaseDexie & T; export const accountsDB = new BaseDexie("KickStartAccounts") as SensitiveDexie;
export const db = new BaseDexie("KickStart") as Dexie; const SensitiveSchemas = Object.assign({}, AccountsSchema);
const AllSchemas = Object.assign(
{}, export type NonsensitiveDexie<T extends unknown = NonsensitiveTables> =
AccountsSchema, BaseDexie & T;
ContactsSchema, export const db = new BaseDexie("KickStart") as NonsensitiveDexie;
SettingsSchema const NonsensitiveSchemas = Object.assign({}, ContactsSchema, SettingsSchema);
);
/** /**
* Needed to enable a special webpack setting to allow *await* below: * Needed to enable a special webpack setting to allow *await* below:
@ -48,11 +49,13 @@ if (localStorage.getItem("secret") == null) {
} }
//console.log("IndexedDB Encryption Secret:", secret); //console.log("IndexedDB Encryption Secret:", secret);
encrypted(db, { secretKey: secret }); encrypted(accountsDB, { secretKey: secret });
db.version(1).stores(AllSchemas); accountsDB.version(1).stores(SensitiveSchemas);
db.version(1).stores(NonsensitiveSchemas);
// initialize, a la https://dexie.org/docs/Tutorial/Design#the-populate-event // initialize, a la https://dexie.org/docs/Tutorial/Design#the-populate-event
db.on("populate", function () { db.on("populate", function () {
// ensure there's an initial entry for settings // ensure there's an initial entry for settings
db.settings.add({ id: MASTER_SETTINGS }); db.settings.add({ id: MASTER_SETTINGS_KEY });
}); });

26
src/db/tables/index.ts → src/db/tables/accounts.ts

@ -13,29 +13,3 @@ export const AccountsSchema = {
accounts: accounts:
"++id, dateCreated, derivationPath, $identity, $mnemonic, publicKeyHex", "++id, dateCreated, derivationPath, $identity, $mnemonic, publicKeyHex",
}; };
export interface Contact {
did: string;
name?: string;
publicKeyBase64?: string;
seesMe?: boolean;
registered?: boolean;
}
export const ContactsSchema = {
contacts: "++did, name, publicKeyBase64, registered, seesMe",
};
// a singleton
export type Settings = {
id: number;
firstName?: string;
lastName?: string;
showContactGivesInline?: boolean;
};
export const SettingsSchema = {
settings: "id",
};
export const MASTER_SETTINGS = 1;

11
src/db/tables/contacts.ts

@ -0,0 +1,11 @@
export interface Contact {
did: string;
name?: string;
publicKeyBase64?: string;
seesMe?: boolean;
registered?: boolean;
}
export const ContactsSchema = {
contacts: "++did, name, publicKeyBase64, registered, seesMe",
};

13
src/db/tables/settings.ts

@ -0,0 +1,13 @@
// a singleton
export type Settings = {
id: number;
firstName?: string;
lastName?: string;
showContactGivesInline?: boolean;
};
export const SettingsSchema = {
settings: "id",
};
export const MASTER_SETTINGS_KEY = 1;

12
src/router/index.ts

@ -1,5 +1,5 @@
import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router"; import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router";
import { db } from "@/db"; import { accountsDB } from "@/db";
const routes: Array<RouteRecordRaw> = [ const routes: Array<RouteRecordRaw> = [
{ {
@ -8,8 +8,8 @@ const routes: Array<RouteRecordRaw> = [
component: () => component: () =>
import(/* webpackChunkName: "start" */ "../views/DiscoverView.vue"), import(/* webpackChunkName: "start" */ "../views/DiscoverView.vue"),
beforeEnter: async (to, from, next) => { beforeEnter: async (to, from, next) => {
await db.open(); await accountsDB.open();
const num_accounts = await db.accounts.count(); const num_accounts = await accountsDB.accounts.count();
if (num_accounts > 0) { if (num_accounts > 0) {
next(); next();
} else { } else {
@ -63,6 +63,12 @@ const routes: Array<RouteRecordRaw> = [
component: () => component: () =>
import(/* webpackChunkName: "discover" */ "../views/DiscoverView.vue"), import(/* webpackChunkName: "discover" */ "../views/DiscoverView.vue"),
}, },
{
path: "/help",
name: "help",
component: () =>
import(/* webpackChunkName: "help" */ "../views/HelpView.vue"),
},
{ {
path: "/import-account", path: "/import-account",
name: "import-account", name: "import-account",

6
src/test/index.ts

@ -1,7 +1,7 @@
import axios from "axios"; import axios from "axios";
import * as didJwt from "did-jwt"; import * as didJwt from "did-jwt";
import { AppString } from "@/constants/app"; import { AppString } from "@/constants/app";
import { db } from "../db"; import { accountsDB } from "../db";
import { SERVICE_ID } from "../libs/veramo/setup"; import { SERVICE_ID } from "../libs/veramo/setup";
import { deriveAddress, newIdentifier } from "../libs/crypto"; import { deriveAddress, newIdentifier } from "../libs/crypto";
@ -13,8 +13,8 @@ export async function testServerRegisterUser() {
const identity0 = newIdentifier(addr, publicHex, privateHex, deriPath); const identity0 = newIdentifier(addr, publicHex, privateHex, deriPath);
await db.open(); await accountsDB.open();
const accounts = await db.accounts.toArray(); const accounts = await accountsDB.accounts.toArray();
const thisIdentity = JSON.parse(accounts[0].identity); const thisIdentity = JSON.parse(accounts[0].identity);
// Make a claim // Make a claim

93
src/views/AccountViewView.vue

@ -26,7 +26,7 @@
<fa icon="folder-open" class="fa-fw"></fa> <fa icon="folder-open" class="fa-fw"></fa>
</router-link> </router-link>
</li> </li>
<!-- Commitments --> <!-- Contacts -->
<li class="basis-1/5 rounded-md text-slate-500"> <li class="basis-1/5 rounded-md text-slate-500">
<router-link <router-link
:to="{ name: 'contacts' }" :to="{ name: 'contacts' }"
@ -54,6 +54,18 @@
Your Identity Your Identity
</h1> </h1>
<div class="flex justify-between py-2">
<span />
<span>
<router-link
:to="{ name: 'help' }"
class="text-xs uppercase bg-blue-500 text-white px-1.5 py-1 rounded-md ml-1"
>
Help
</router-link>
</span>
</div>
<!-- Friend referral requirement notice --> <!-- Friend referral requirement notice -->
<div <div
class="bg-amber-200 text-amber-900 border-amber-500 border-dashed border text-center rounded-md overflow-hidden px-4 py-3 mb-4" class="bg-amber-200 text-amber-900 border-amber-500 border-dashed border text-center rounded-md overflow-hidden px-4 py-3 mb-4"
@ -122,29 +134,34 @@
<router-link <router-link
:to="{ name: 'new-edit-account' }" :to="{ name: 'new-edit-account' }"
class="block text-center text-lg font-bold uppercase bg-blue-600 text-white px-2 py-3 rounded-md mb-8" class="block text-center text-lg font-bold uppercase bg-blue-600 text-white px-2 py-3 rounded-md mb-8"
>Edit Identity</router-link
> >
Edit Identity
</router-link>
<h3 class="text-sm uppercase font-semibold mb-3">Contact Actions</h3> <h3 class="text-sm uppercase font-semibold mb-3">Contact Actions</h3>
<a <a
href="contact-scan.html" href="contact-scan.html"
class="block w-full text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md mb-6" class="block w-full text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md mb-6"
>Scan New Contact</a
> >
Scan New Contact
</a>
<h3 class="text-sm uppercase font-semibold mb-3">Identity Actions</h3> <h3 class="text-sm uppercase font-semibold mb-3">Data</h3>
<a <a
href="" href=""
class="block w-full text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md mb-2" class="block w-full text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md mb-2"
>Backup Seed</a
> >
Backup Identifier Seed
</a>
<a <a
href=""
class="block w-full text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md mb-6" class="block w-full text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md mb-6"
>Backup Other Data</a @click="exportDatabase()"
> >
Download Settings & Contacts (excluding Identifier Data)
</a>
<a ref="downloadLink" />
<!-- QR code popup --> <!-- QR code popup -->
<dialog id="dlgQR" class="backdrop:bg-black/75 rounded-md"> <dialog id="dlgQR" class="backdrop:bg-black/75 rounded-md">
@ -198,10 +215,11 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import "dexie-export-import";
import { Options, Vue } from "vue-class-component"; import { Options, Vue } from "vue-class-component";
import { useClipboard } from "@vueuse/core"; import { useClipboard } from "@vueuse/core";
import { db } from "@/db"; import { db, accountsDB } from "@/db";
import { MASTER_SETTINGS } from "@/db/tables"; import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
import { deriveAddress, generateSeed, newIdentifier } from "@/libs/crypto"; import { deriveAddress, generateSeed, newIdentifier } from "@/libs/crypto";
//import { testServerRegisterUser } from "../test"; //import { testServerRegisterUser } from "../test";
@ -209,9 +227,6 @@ import { deriveAddress, generateSeed, newIdentifier } from "@/libs/crypto";
components: {}, components: {},
}) })
export default class AccountViewView extends Vue { export default class AccountViewView extends Vue {
// This registers current user in vue plugin with: $vm.ctx.testRegisterUser()
//testRegisterUser = testServerRegisterUser;
address = ""; address = "";
firstName = ""; firstName = "";
lastName = ""; lastName = "";
@ -224,16 +239,23 @@ export default class AccountViewView extends Vue {
// 'created' hook runs when the Vue instance is first created // 'created' hook runs when the Vue instance is first created
async created() { async created() {
await db.open(); // Uncomment to register this user on the test server.
// To manage within the vue devtools browser extension https://devtools.vuejs.org/
// assign this to a class variable, eg. "registerThisUser = testServerRegisterUser",
// select a component in the extension, and enter in the console: $vm.ctx.registerThisUser()
//testServerRegisterUser();
try { try {
const settings = await db.settings.get(MASTER_SETTINGS); await db.open();
const settings = await db.settings.get(MASTER_SETTINGS_KEY);
if (settings) { if (settings) {
this.firstName = settings.firstName || ""; this.firstName = settings.firstName || "";
this.lastName = settings.lastName || ""; this.lastName = settings.lastName || "";
this.showContactGives = !!settings.showContactGivesInline; this.showContactGives = !!settings.showContactGivesInline;
} }
const numAccounts = await db.accounts.count(); await accountsDB.open();
const numAccounts = await accountsDB.accounts.count();
if (numAccounts === 0) { if (numAccounts === 0) {
let privateHex = ""; let privateHex = "";
this.mnemonic = generateSeed(); this.mnemonic = generateSeed();
@ -246,7 +268,7 @@ export default class AccountViewView extends Vue {
privateHex, privateHex,
this.derivationPath this.derivationPath
); );
await db.accounts.add({ await accountsDB.accounts.add({
dateCreated: new Date().toISOString(), dateCreated: new Date().toISOString(),
derivationPath: this.derivationPath, derivationPath: this.derivationPath,
identity: JSON.stringify(newId), identity: JSON.stringify(newId),
@ -254,6 +276,12 @@ export default class AccountViewView extends Vue {
publicKeyHex: newId.keys[0].publicKeyHex, publicKeyHex: newId.keys[0].publicKeyHex,
}); });
} }
const accounts = await accountsDB.accounts.toArray();
const identity = JSON.parse(accounts[0].identity);
this.address = identity.did;
this.publicHex = identity.keys[0].publicKeyHex;
this.derivationPath = identity.keys[0].meta.derivationPath;
} catch (err) { } catch (err) {
this.alertMessage = this.alertMessage =
"Clear your cache and start over (after data backup). See console log for more info."; "Clear your cache and start over (after data backup). See console log for more info.";
@ -261,21 +289,15 @@ export default class AccountViewView extends Vue {
this.alertTitle = "Error Creating Account"; this.alertTitle = "Error Creating Account";
this.isAlertVisible = true; this.isAlertVisible = true;
} }
const accounts = await db.accounts.toArray();
const identity = JSON.parse(accounts[0].identity);
this.address = identity.did;
this.publicHex = identity.keys[0].publicKeyHex;
this.derivationPath = identity.keys[0].meta.derivationPath;
} }
public async toggleShowContactAmounts() { public async toggleShowContactAmounts() {
this.showContactGives = !this.showContactGives; this.showContactGives = !this.showContactGives;
try { try {
await db.open(); await db.open();
const settings = await db.settings.get(MASTER_SETTINGS); const settings = await db.settings.get(MASTER_SETTINGS_KEY);
if (settings) { if (settings) {
db.settings.update(MASTER_SETTINGS, { db.settings.update(MASTER_SETTINGS_KEY, {
showContactGivesInline: this.showContactGives, showContactGivesInline: this.showContactGives,
}); });
} }
@ -295,6 +317,29 @@ export default class AccountViewView extends Vue {
}; };
} }
public async exportDatabase() {
try {
const blob = await db.export({ prettyJson: true });
const url = URL.createObjectURL(blob);
const downloadAnchor = this.$refs.downloadLink as HTMLAnchorElement;
downloadAnchor.href = url;
downloadAnchor.download = db.name + "-backup.json";
downloadAnchor.click();
URL.revokeObjectURL(url);
this.alertTitle = "Download Started";
this.alertMessage = "See your downloads directory for the backup.";
this.isAlertVisible = true;
} catch (error) {
this.alertTitle = "Export Error";
this.alertMessage = "See console logs for more info.";
this.isAlertVisible = true;
console.error("Export Error:", error);
}
}
alertMessage = ""; alertMessage = "";
alertTitle = ""; alertTitle = "";
isAlertVisible = false; isAlertVisible = false;

12
src/views/ContactsView.vue

@ -24,7 +24,7 @@
><fa icon="folder-open" class="fa-fw"></fa ><fa icon="folder-open" class="fa-fw"></fa
></router-link> ></router-link>
</li> </li>
<!-- Commitments --> <!-- Contacts -->
<li class="basis-1/5 rounded-md text-slate-500"> <li class="basis-1/5 rounded-md text-slate-500">
<router-link <router-link
:to="{ name: 'contacts' }" :to="{ name: 'contacts' }"
@ -140,8 +140,8 @@ import { Options, Vue } from "vue-class-component";
import { AppString } from "@/constants/app"; import { AppString } from "@/constants/app";
import { accessToken, SimpleSigner } from "@/libs/crypto"; import { accessToken, SimpleSigner } from "@/libs/crypto";
import { IIdentifier } from "@veramo/core"; import { IIdentifier } from "@veramo/core";
import { db } from "../db"; import { accountsDB, db } from "../db";
import { Contact } from "../db/tables"; import { Contact } from "../db/tables/contacts";
export interface GiveVerifiableCredential { export interface GiveVerifiableCredential {
"@context": string; "@context": string;
@ -169,9 +169,11 @@ export default class ContactsView extends Vue {
// 'created' hook runs when the Vue instance is first created // 'created' hook runs when the Vue instance is first created
async created() { async created() {
await db.open(); await accountsDB.open();
const accounts = await db.accounts.toArray(); const accounts = await accountsDB.accounts.toArray();
this.identity = JSON.parse(accounts[0].identity); this.identity = JSON.parse(accounts[0].identity);
await db.open();
this.contacts = await db.contacts.toArray(); this.contacts = await db.contacts.toArray();
const params = new URLSearchParams(window.location.search); const params = new URLSearchParams(window.location.search);

2
src/views/DiscoverView.vue

@ -24,7 +24,7 @@
><fa icon="folder-open" class="fa-fw"></fa ><fa icon="folder-open" class="fa-fw"></fa
></router-link> ></router-link>
</li> </li>
<!-- Commitments --> <!-- Contacts -->
<li class="basis-1/5 rounded-md text-slate-500"> <li class="basis-1/5 rounded-md text-slate-500">
<router-link <router-link
:to="{ name: 'contacts' }" :to="{ name: 'contacts' }"

139
src/views/HelpView.vue

@ -0,0 +1,139 @@
<template>
<!-- QUICK NAV -->
<nav id="QuickNav" class="fixed bottom-0 left-0 right-0 bg-slate-200">
<ul class="flex text-2xl p-2 gap-2">
<!-- Home Feed -->
<li class="basis-1/5 rounded-md text-slate-500">
<router-link :to="{ name: 'home' }" class="block text-center py-3 px-1">
<fa icon="house-chimney" class="fa-fw"></fa>
</router-link>
</li>
<!-- Search -->
<li class="basis-1/5 rounded-md text-slate-500">
<router-link
:to="{ name: 'discover' }"
class="block text-center py-3 px-1"
>
<fa icon="magnifying-glass" class="fa-fw"></fa>
</router-link>
</li>
<!-- Projects -->
<li class="basis-1/5 rounded-md text-slate-500">
<router-link
:to="{ name: 'projects' }"
class="block text-center py-3 px-1"
>
<fa icon="folder-open" class="fa-fw"></fa>
</router-link>
</li>
<!-- Contacts -->
<li class="basis-1/5 rounded-md text-slate-500">
<router-link
:to="{ name: 'contacts' }"
class="block text-center py-3 px-1"
>
<fa icon="users" class="fa-fw"></fa>
</router-link>
</li>
<!-- Profile -->
<li class="basis-1/5 rounded-md text-slate-400">
<router-link
:to="{ name: 'account' }"
class="block text-center py-3 px-1"
>
<fa icon="circle-user" class="fa-fw"></fa>
</router-link>
</li>
</ul>
</nav>
<!-- CONTENT -->
<section id="Content" class="p-6 pb-24">
<!-- Heading -->
<h1 id="ViewHeading" class="text-4xl text-center font-light pt-4 mb-8">
Help
</h1>
<div>
<h2 class="text-xl font-semibold">Introduction</h2>
<p>
This app is a window into data that you and your friends own, focused on
gifts and collaboration.
</p>
<h2 class="text-xl font-semibold">How do I backup all my data?</h2>
<p>
There are two parts to backup your data: the identifier secrets and the
other data such as settings, contacts, etc.
</p>
<div class="px-4">
<h2 class="text-xl font-semibold">
How do I backup my identifier (secret) data?
</h2>
<ul class="list-disc list-inside">
<li>
Go to Your Identity <fa icon="circle-user" class="fa-fw" /> page.
</li>
<li>
Click on "Backup Identifier Seed" and follow the instructions.
</li>
</ul>
<h2 class="text-xl font-semibold">
How do I backup my other (non-identifier-secret) data?
</h2>
<ul class="list-disc list-inside">
<li>
Go to Your Identity <fa icon="circle-user" class="fa-fw" /> page.
</li>
<li>
Click on "Download Settings...". That will save a file to your
downloads folder. That is your backup, so put it someplace where you
won't lose it.
</li>
</ul>
</div>
<h2 class="text-xl font-semibold">How do I restore my data?</h2>
<p>
There are two parts to restore your data: the identity secrets and the
other data such as settings, contacts, etc.
</p>
<div class="px-4">
<h2 class="text-xl font-semibold">
How do I restore my identifier (secret) data?
</h2>
<ul class="list-disc list-inside">
<li>
You only have one identifier at a time. If you have an identifier on
Your Identity <fa icon="circle-user" class="fa-fw" /> page, you'll
need to clear it out;
<a
href="https://www.lifewire.com/how-to-clear-cache-2617980"
class="text-blue-500"
>
here are some helpful instructions.
</a>
But beware! This will also clear out your settings and contact data,
so be sure to back that up first.
</li>
<li>
<router-link class="text-blue-500" to="/">
Go to the start
</router-link>
and choose "Yes" to enter the identity you backed up.
</li>
</ul>
<h2 class="text-xl font-semibold">
How do I restore my other (non-identifier-secret) data?
</h2>
<ul class="list-disc list-inside">
<li>Make sure you have your backup file (above), then contact us.</li>
</ul>
</div>
</div>
</section>
</template>

8
src/views/ImportAccountView.vue

@ -45,7 +45,7 @@
<script lang="ts"> <script lang="ts">
import { Options, Vue } from "vue-class-component"; import { Options, Vue } from "vue-class-component";
import { deriveAddress, newIdentifier } from "../libs/crypto"; import { deriveAddress, newIdentifier } from "../libs/crypto";
import { db } from "@/db"; import { accountsDB } from "@/db";
@Options({ @Options({
components: {}, components: {},
@ -75,10 +75,10 @@ export default class ImportAccountView extends Vue {
); );
try { try {
await db.open(); await accountsDB.open();
const num_accounts = await db.accounts.count(); const num_accounts = await accountsDB.accounts.count();
if (num_accounts === 0) { if (num_accounts === 0) {
await db.accounts.add({ await accountsDB.accounts.add({
dateCreated: new Date().toISOString(), dateCreated: new Date().toISOString(),
derivationPath: this.derivationPath, derivationPath: this.derivationPath,
identity: JSON.stringify(newId), identity: JSON.stringify(newId),

6
src/views/NewEditAccountView.vue

@ -51,7 +51,7 @@
<script lang="ts"> <script lang="ts">
import { Options, Vue } from "vue-class-component"; import { Options, Vue } from "vue-class-component";
import { db } from "@/db"; import { db } from "@/db";
import { MASTER_SETTINGS } from "@/db/tables"; import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
@Options({ @Options({
components: {}, components: {},
@ -69,7 +69,7 @@ export default class NewEditAccountView extends Vue {
// 'created' hook runs when the Vue instance is first created // 'created' hook runs when the Vue instance is first created
async created() { async created() {
await db.open(); await db.open();
const settings = await db.settings.get(MASTER_SETTINGS); const settings = await db.settings.get(MASTER_SETTINGS_KEY);
if (settings) { if (settings) {
this.firstName = settings.firstName || ""; this.firstName = settings.firstName || "";
this.lastName = settings.lastName || ""; this.lastName = settings.lastName || "";
@ -77,7 +77,7 @@ export default class NewEditAccountView extends Vue {
} }
onClickSaveChanges() { onClickSaveChanges() {
db.settings.update(MASTER_SETTINGS, { db.settings.update(MASTER_SETTINGS_KEY, {
firstName: this.firstName, firstName: this.firstName,
lastName: this.lastName, lastName: this.lastName,
}); });

29
src/views/NewEditProjectView.vue

@ -78,7 +78,7 @@
<script lang="ts"> <script lang="ts">
import { Options, Vue } from "vue-class-component"; import { Options, Vue } from "vue-class-component";
import { AppString } from "@/constants/app"; import { AppString } from "@/constants/app";
import { db } from "../db"; import { accountsDB } from "../db";
import { accessToken, SimpleSigner } from "@/libs/crypto"; import { accessToken, SimpleSigner } from "@/libs/crypto";
import * as didJwt from "did-jwt"; import * as didJwt from "did-jwt";
import { IIdentifier } from "@veramo/core"; import { IIdentifier } from "@veramo/core";
@ -112,12 +112,12 @@ export default class NewEditProjectView extends Vue {
if (this.projectId === "") { if (this.projectId === "") {
console.log("This is a new project"); console.log("This is a new project");
} else { } else {
await db.open(); await accountsDB.open();
const num_accounts = await db.accounts.count(); const num_accounts = await accountsDB.accounts.count();
if (num_accounts === 0) { if (num_accounts === 0) {
console.log("Problem! Should have a profile!"); console.log("Problem! Should have a profile!");
} else { } else {
const accounts = await db.accounts.toArray(); const accounts = await accountsDB.accounts.toArray();
const identity = JSON.parse(accounts[0].identity); const identity = JSON.parse(accounts[0].identity);
this.LoadProject(identity); this.LoadProject(identity);
} }
@ -126,8 +126,10 @@ export default class NewEditProjectView extends Vue {
async LoadProject(identity: IIdentifier) { async LoadProject(identity: IIdentifier) {
const endorserApiServer = AppString.DEFAULT_ENDORSER_API_SERVER; const endorserApiServer = AppString.DEFAULT_ENDORSER_API_SERVER;
// eslint-disable-next-line prettier/prettier const url =
const url = endorserApiServer + "/api/claim/byHandle/" + encodeURIComponent(this.projectId); endorserApiServer +
"/api/claim/byHandle/" +
encodeURIComponent(this.projectId);
const token = await accessToken(identity); const token = await accessToken(identity);
const headers = { const headers = {
"Content-Type": "application/json", "Content-Type": "application/json",
@ -161,7 +163,6 @@ export default class NewEditProjectView extends Vue {
} }
// Make a payload for the claim // Make a payload for the claim
const vcPayload = { const vcPayload = {
sub: "PlanAction",
vc: { vc: {
"@context": ["https://www.w3.org/2018/credentials/v1"], "@context": ["https://www.w3.org/2018/credentials/v1"],
type: ["VerifiableCredential"], type: ["VerifiableCredential"],
@ -169,12 +170,8 @@ export default class NewEditProjectView extends Vue {
}, },
}; };
// create a signature using private key of identity // create a signature using private key of identity
if ( if (identity.keys[0].privateKeyHex != null) {
identity.keys[0].privateKeyHex !== "undefined" && const privateKeyHex: string = identity.keys[0].privateKeyHex;
identity.keys[0].privateKeyHex !== null
) {
// eslint-disable-next-line
const privateKeyHex: string = identity.keys[0].privateKeyHex!;
const signer = await SimpleSigner(privateKeyHex); const signer = await SimpleSigner(privateKeyHex);
const alg = undefined; const alg = undefined;
// create a JWT for the request // create a JWT for the request
@ -254,12 +251,12 @@ export default class NewEditProjectView extends Vue {
public async onSaveProjectClick() { public async onSaveProjectClick() {
this.isHiddenSave = true; this.isHiddenSave = true;
this.isHiddenSpinner = false; this.isHiddenSpinner = false;
await db.open(); await accountsDB.open();
const num_accounts = await db.accounts.count(); const num_accounts = await accountsDB.accounts.count();
if (num_accounts === 0) { if (num_accounts === 0) {
console.log("Problem! Should have a profile!"); console.log("Problem! Should have a profile!");
} else { } else {
const accounts = await db.accounts.toArray(); const accounts = await accountsDB.accounts.toArray();
const identity = JSON.parse(accounts[0].identity); const identity = JSON.parse(accounts[0].identity);
this.SaveProject(identity); this.SaveProject(identity);
} }

14
src/views/ProjectViewView.vue

@ -24,9 +24,11 @@
><fa icon="folder-open" class="fa-fw"></fa ><fa icon="folder-open" class="fa-fw"></fa
></router-link> ></router-link>
</li> </li>
<!-- Commitments --> <!-- Contacts -->
<li class="basis-1/5 rounded-md text-slate-500"> <li class="basis-1/5 rounded-md text-slate-500">
<router-link :to="{ name: '' }" class="block text-center py-3 px-1" <router-link
:to="{ name: 'contacts' }"
class="block text-center py-3 px-1"
><fa icon="hand" class="fa-fw"></fa ><fa icon="hand" class="fa-fw"></fa
></router-link> ></router-link>
</li> </li>
@ -146,7 +148,7 @@
<script lang="ts"> <script lang="ts">
import { Options, Vue } from "vue-class-component"; import { Options, Vue } from "vue-class-component";
import { accessToken } from "@/libs/crypto"; import { accessToken } from "@/libs/crypto";
import { db } from "../db"; import { accountsDB } from "../db";
import { IIdentifier } from "@veramo/core"; import { IIdentifier } from "@veramo/core";
import { AppString } from "@/constants/app"; import { AppString } from "@/constants/app";
import * as moment from "moment"; import * as moment from "moment";
@ -224,12 +226,12 @@ export default class ProjectViewView extends Vue {
// 'created' hook runs when the Vue instance is first created // 'created' hook runs when the Vue instance is first created
async created() { async created() {
await db.open(); await accountsDB.open();
const num_accounts = await db.accounts.count(); const num_accounts = await accountsDB.accounts.count();
if (num_accounts === 0) { if (num_accounts === 0) {
console.log("Problem! Should have a profile!"); console.log("Problem! Should have a profile!");
} else { } else {
const accounts = await db.accounts.toArray(); const accounts = await accountsDB.accounts.toArray();
const identity = JSON.parse(accounts[0].identity); const identity = JSON.parse(accounts[0].identity);
this.LoadProject(identity); this.LoadProject(identity);
} }

10
src/views/ProjectsView.vue

@ -24,7 +24,7 @@
><fa icon="folder-open" class="fa-fw"></fa ><fa icon="folder-open" class="fa-fw"></fa
></router-link> ></router-link>
</li> </li>
<!-- Commitments --> <!-- Contacts -->
<li class="basis-1/5 rounded-md text-slate-500"> <li class="basis-1/5 rounded-md text-slate-500">
<router-link <router-link
:to="{ name: 'contacts' }" :to="{ name: 'contacts' }"
@ -103,7 +103,7 @@
<script lang="ts"> <script lang="ts">
import { Options, Vue } from "vue-class-component"; import { Options, Vue } from "vue-class-component";
import { accessToken } from "@/libs/crypto"; import { accessToken } from "@/libs/crypto";
import { db } from "../db"; import { accountsDB } from "../db";
import { IIdentifier } from "@veramo/core"; import { IIdentifier } from "@veramo/core";
import { AppString } from "@/constants/app"; import { AppString } from "@/constants/app";
@ -155,12 +155,12 @@ export default class ProjectsView extends Vue {
// 'created' hook runs when the Vue instance is first created // 'created' hook runs when the Vue instance is first created
async created() { async created() {
await db.open(); await accountsDB.open();
const num_accounts = await db.accounts.count(); const num_accounts = await accountsDB.accounts.count();
if (num_accounts === 0) { if (num_accounts === 0) {
console.log("Problem! Should have a profile!"); console.log("Problem! Should have a profile!");
} else { } else {
const accounts = await db.accounts.toArray(); const accounts = await accountsDB.accounts.toArray();
const identity = JSON.parse(accounts[0].identity); const identity = JSON.parse(accounts[0].identity);
this.LoadProjects(identity); this.LoadProjects(identity);
} }

Loading…
Cancel
Save