1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
|
import { Stores, WALLET_DB_VERSION } from "./types/dbTypes";
import { Store, Index } from "./util/query";
const DB_NAME = "taler";
/**
* Return a promise that resolves
* to the taler wallet db.
*/
export function openDatabase(
idbFactory: IDBFactory,
onVersionChange: () => void,
onUpgradeUnsupported: (oldVersion: number, newVersion: number) => void,
): Promise<IDBDatabase> {
return new Promise<IDBDatabase>((resolve, reject) => {
const req = idbFactory.open(DB_NAME, WALLET_DB_VERSION);
req.onerror = e => {
console.log("taler database error", e);
reject(new Error("database error"));
};
req.onsuccess = e => {
req.result.onversionchange = (evt: IDBVersionChangeEvent) => {
console.log(
`handling live db version change from ${evt.oldVersion} to ${evt.newVersion}`,
);
req.result.close();
onVersionChange();
};
resolve(req.result);
};
req.onupgradeneeded = e => {
const db = req.result;
console.log(
`DB: upgrade needed: oldVersion=${e.oldVersion}, newVersion=${e.newVersion}`,
);
switch (e.oldVersion) {
case 0: // DB does not exist yet
for (const n in Stores) {
if ((Stores as any)[n] instanceof Store) {
const si: Store<any> = (Stores as any)[n];
const s = db.createObjectStore(si.name, si.storeParams);
for (const indexName in si as any) {
if ((si as any)[indexName] instanceof Index) {
const ii: Index<any, any> = (si as any)[indexName];
s.createIndex(ii.indexName, ii.keyPath, ii.options);
}
}
}
}
break;
default:
if (e.oldVersion !== WALLET_DB_VERSION) {
onUpgradeUnsupported(e.oldVersion, WALLET_DB_VERSION);
throw Error("incompatible DB");
}
break;
}
};
});
}
export function exportDatabase(db: IDBDatabase): Promise<any> {
const dump = {
name: db.name,
stores: {} as { [s: string]: any },
version: db.version,
};
return new Promise((resolve, reject) => {
const tx = db.transaction(Array.from(db.objectStoreNames));
tx.addEventListener("complete", () => {
resolve(dump);
});
// tslint:disable-next-line:prefer-for-of
for (let i = 0; i < db.objectStoreNames.length; i++) {
const name = db.objectStoreNames[i];
const storeDump = {} as { [s: string]: any };
dump.stores[name] = storeDump;
tx.objectStore(name)
.openCursor()
.addEventListener("success", (e: Event) => {
const cursor = (e.target as any).result;
if (cursor) {
storeDump[cursor.key] = cursor.value;
cursor.continue();
}
});
}
});
}
export function importDatabase(db: IDBDatabase, dump: any): Promise<void> {
console.log("importing db", dump);
return new Promise<void>((resolve, reject) => {
const tx = db.transaction(Array.from(db.objectStoreNames), "readwrite");
if (dump.stores) {
for (const storeName in dump.stores) {
const objects = [];
const dumpStore = dump.stores[storeName];
for (const key in dumpStore) {
objects.push(dumpStore[key]);
}
console.log(`importing ${objects.length} records into ${storeName}`);
const store = tx.objectStore(storeName);
for (const obj of objects) {
store.put(obj);
}
}
}
tx.addEventListener("complete", () => {
resolve();
});
});
}
export function deleteDatabase(idbFactory: IDBFactory) {
idbFactory.deleteDatabase(DB_NAME);
}
|