Browse Source

separate account from other data for backup/restore

kb/add-usage-guide
Trent Larson 2 years ago
parent
commit
45b54db01e
  1. 4
      README.md
  2. 41
      src/db/index.ts
  3. 26
      src/db/tables/accounts.ts
  4. 11
      src/db/tables/contacts.ts
  5. 13
      src/db/tables/settings.ts
  6. 6
      src/router/index.ts
  7. 6
      src/test/index.ts
  8. 33
      src/views/AccountViewView.vue
  9. 10
      src/views/ContactsView.vue
  10. 8
      src/views/ImportAccountView.vue
  11. 6
      src/views/NewEditAccountView.vue
  12. 14
      src/views/NewEditProjectView.vue
  13. 8
      src/views/ProjectViewView.vue
  14. 8
      src/views/ProjectsView.vue

4
README.md

@ -48,9 +48,9 @@ On the test server, User #0 has rights to register others, so you can start play
- Register someone else under User #0 on the `/account` page: - Register someone else under User #0 on the `/account` page:
* Edit the `src/views/AccountViewView.vue` file and uncomment the lines referring to "test". * Edit the `src/views/AccountViewView.vue` file and 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, open the [Vue Devtools browser extension](https://devtools.vuejs.org/), select a component, then type this into the console: `$vm.ctx.testRegisterUser()`

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("KickStartSensitive") 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;

6
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 {

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

33
src/views/AccountViewView.vue

@ -200,17 +200,17 @@
<script lang="ts"> <script lang="ts">
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";
@Options({ @Options({
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() // This registers current user in vue plugin with: $vm.ctx.testRegisterUser()
//testRegisterUser = testServerRegisterUser; testRegisterUser = testServerRegisterUser;
address = ""; address = "";
firstName = ""; firstName = "";
@ -224,16 +224,17 @@ 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();
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 +247,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 +255,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 +268,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,
}); });
} }

10
src/views/ContactsView.vue

@ -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);

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,
}); });

14
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);
} }
@ -254,12 +254,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);
} }

8
src/views/ProjectViewView.vue

@ -146,7 +146,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 +224,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);
} }

8
src/views/ProjectsView.vue

@ -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