summaryrefslogtreecommitdiff
path: root/packages/taler-wallet-core/src/db.ts
blob: b13abac579c6c80b047e8e7fd69c93bebe8e37d6 (plain)
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
import { Stores } from "./types/dbTypes";
import { openDatabase, Database, Store, Index } from "./util/query";
import {
  IDBFactory,
  IDBDatabase,
  IDBObjectStore,
  IDBTransaction,
} from "idb-bridge";
import { Logger } from "./util/logging";

/**
 * Name of the Taler database.  This is effectively the major
 * version of the DB schema. Whenever it changes, custom import logic
 * for all previous versions must be written, which should be
 * avoided.
 */
const TALER_DB_NAME = "taler-wallet-prod-v1";

/**
 * Current database minor version, should be incremented
 * each time we do minor schema changes on the database.
 * A change is considered minor when fields are added in a
 * backwards-compatible way or object stores and indices
 * are added.
 */
export const WALLET_DB_MINOR_VERSION = 3;

const logger = new Logger("db.ts");

/**
 * Return a promise that resolves
 * to the taler wallet db.
 */
export function openTalerDatabase(
  idbFactory: IDBFactory,
  onVersionChange: () => void,
): Promise<IDBDatabase> {
  const onUpgradeNeeded = (
    db: IDBDatabase,
    oldVersion: number,
    newVersion: number,
    upgradeTransaction: IDBTransaction,
  ): void => {
    if (oldVersion === 0) {
      for (const n in Stores) {
        if ((Stores as any)[n] instanceof Store) {
          const si: Store<string, 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<string, string, any, any> = (si as any)[
                indexName
              ];
              s.createIndex(ii.indexName, ii.keyPath, ii.options);
            }
          }
        }
      }
      return;
    }
    if (oldVersion === newVersion) {
      return;
    }
    logger.info(`upgrading database from ${oldVersion} to ${newVersion}`);
    for (const n in Stores) {
      if ((Stores as any)[n] instanceof Store) {
        const si: Store<string, any> = (Stores as any)[n];
        let s: IDBObjectStore;
        const storeVersionAdded = si.storeParams?.versionAdded ?? 1;
        if (storeVersionAdded > oldVersion) {
          s = db.createObjectStore(si.name, si.storeParams);
        } else {
          s = upgradeTransaction.objectStore(si.name);
        }
        for (const indexName in si as any) {
          if ((si as any)[indexName] instanceof Index) {
            const ii: Index<string, string, any, any> = (si as any)[indexName];
            const indexVersionAdded = ii.options?.versionAdded ?? 0;
            if (
              indexVersionAdded > oldVersion ||
              storeVersionAdded > oldVersion
            ) {
              s.createIndex(ii.indexName, ii.keyPath, ii.options);
            }
          }
        }
      }
    }
  };

  return openDatabase(
    idbFactory,
    TALER_DB_NAME,
    WALLET_DB_MINOR_VERSION,
    onVersionChange,
    onUpgradeNeeded,
  );
}

export function deleteTalerDatabase(idbFactory: IDBFactory): void {
  Database.deleteDatabase(idbFactory, TALER_DB_NAME);
}