2021-01-13 00:50:56 +01:00
|
|
|
import { MetaStores, Stores } from "./types/dbTypes";
|
|
|
|
import {
|
|
|
|
openDatabase,
|
|
|
|
Database,
|
|
|
|
Store,
|
|
|
|
Index,
|
|
|
|
AnyStoreMap,
|
|
|
|
} from "./util/query";
|
2020-12-02 14:55:04 +01:00
|
|
|
import {
|
|
|
|
IDBFactory,
|
|
|
|
IDBDatabase,
|
|
|
|
IDBObjectStore,
|
|
|
|
IDBTransaction,
|
|
|
|
} from "idb-bridge";
|
|
|
|
import { Logger } from "./util/logging";
|
2019-07-31 01:33:56 +02:00
|
|
|
|
2020-05-08 14:15:23 +02:00
|
|
|
/**
|
2020-09-08 17:46:11 +02:00
|
|
|
* 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.
|
2020-05-08 14:15:23 +02:00
|
|
|
*/
|
2021-01-13 00:50:56 +01:00
|
|
|
const TALER_DB_NAME = "taler-wallet-main-v2";
|
|
|
|
|
|
|
|
const TALER_META_DB_NAME = "taler-wallet-meta";
|
|
|
|
|
|
|
|
const CURRENT_DB_CONFIG_KEY = "currentMainDbName";
|
2019-07-31 01:33:56 +02:00
|
|
|
|
2019-12-13 13:10:20 +01:00
|
|
|
/**
|
2020-05-08 14:15:23 +02:00
|
|
|
* 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.
|
2019-12-13 13:10:20 +01:00
|
|
|
*/
|
2021-01-13 00:50:56 +01:00
|
|
|
export const WALLET_DB_MINOR_VERSION = 1;
|
2020-11-16 14:12:37 +01:00
|
|
|
|
|
|
|
const logger = new Logger("db.ts");
|
2019-12-13 13:10:20 +01:00
|
|
|
|
2021-01-13 00:50:56 +01:00
|
|
|
function upgradeFromStoreMap(
|
|
|
|
storeMap: AnyStoreMap,
|
|
|
|
db: IDBDatabase,
|
|
|
|
oldVersion: number,
|
|
|
|
newVersion: number,
|
|
|
|
upgradeTransaction: IDBTransaction,
|
|
|
|
): void {
|
|
|
|
if (oldVersion === 0) {
|
|
|
|
for (const n in storeMap) {
|
|
|
|
if ((storeMap as any)[n] instanceof Store) {
|
|
|
|
const si: Store<string, any> = (storeMap 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);
|
2020-11-16 14:12:37 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-01-13 00:50:56 +01:00
|
|
|
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);
|
2019-12-19 13:48:37 +01:00
|
|
|
}
|
|
|
|
}
|
2020-11-16 14:12:37 +01:00
|
|
|
}
|
2019-12-19 13:48:37 +01:00
|
|
|
}
|
2021-01-13 00:50:56 +01:00
|
|
|
}
|
|
|
|
}
|
2019-12-19 13:48:37 +01:00
|
|
|
|
2021-01-13 00:50:56 +01:00
|
|
|
function onTalerDbUpgradeNeeded(
|
|
|
|
db: IDBDatabase,
|
|
|
|
oldVersion: number,
|
|
|
|
newVersion: number,
|
|
|
|
upgradeTransaction: IDBTransaction,
|
|
|
|
) {
|
|
|
|
upgradeFromStoreMap(Stores, db, oldVersion, newVersion, upgradeTransaction);
|
|
|
|
}
|
|
|
|
|
|
|
|
function onMetaDbUpgradeNeeded(
|
|
|
|
db: IDBDatabase,
|
|
|
|
oldVersion: number,
|
|
|
|
newVersion: number,
|
|
|
|
upgradeTransaction: IDBTransaction,
|
|
|
|
) {
|
|
|
|
upgradeFromStoreMap(
|
|
|
|
MetaStores,
|
|
|
|
db,
|
|
|
|
oldVersion,
|
|
|
|
newVersion,
|
|
|
|
upgradeTransaction,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return a promise that resolves
|
|
|
|
* to the taler wallet db.
|
|
|
|
*/
|
|
|
|
export async function openTalerDatabase(
|
|
|
|
idbFactory: IDBFactory,
|
|
|
|
onVersionChange: () => void,
|
|
|
|
): Promise<Database<typeof Stores>> {
|
|
|
|
const metaDbHandle = await openDatabase(
|
|
|
|
idbFactory,
|
|
|
|
TALER_META_DB_NAME,
|
|
|
|
1,
|
|
|
|
() => {},
|
|
|
|
onMetaDbUpgradeNeeded,
|
|
|
|
);
|
|
|
|
|
|
|
|
const metaDb = new Database(metaDbHandle, MetaStores);
|
|
|
|
let currentMainVersion: string | undefined;
|
|
|
|
await metaDb.runWithWriteTransaction([MetaStores.metaConfig], async (tx) => {
|
|
|
|
const dbVersionRecord = await tx.get(
|
|
|
|
MetaStores.metaConfig,
|
|
|
|
CURRENT_DB_CONFIG_KEY,
|
|
|
|
);
|
|
|
|
if (!dbVersionRecord) {
|
|
|
|
currentMainVersion = TALER_DB_NAME;
|
|
|
|
await tx.put(MetaStores.metaConfig, {
|
|
|
|
key: CURRENT_DB_CONFIG_KEY,
|
|
|
|
value: TALER_DB_NAME,
|
|
|
|
});
|
|
|
|
} else {
|
2021-01-13 00:55:47 +01:00
|
|
|
currentMainVersion = dbVersionRecord.value;
|
2021-01-13 00:50:56 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
if (currentMainVersion !== TALER_DB_NAME) {
|
|
|
|
// In the future, the migration logic will be implemented here.
|
|
|
|
throw Error(`migration from database ${currentMainVersion} not supported`);
|
|
|
|
}
|
|
|
|
|
|
|
|
const mainDbHandle = await openDatabase(
|
2019-12-12 22:39:45 +01:00
|
|
|
idbFactory,
|
|
|
|
TALER_DB_NAME,
|
2020-07-09 15:26:18 +02:00
|
|
|
WALLET_DB_MINOR_VERSION,
|
2019-12-12 22:39:45 +01:00
|
|
|
onVersionChange,
|
2021-01-13 00:50:56 +01:00
|
|
|
onTalerDbUpgradeNeeded,
|
2019-12-12 22:39:45 +01:00
|
|
|
);
|
2021-01-13 00:50:56 +01:00
|
|
|
|
|
|
|
return new Database(mainDbHandle, Stores);
|
2019-07-31 01:33:56 +02:00
|
|
|
}
|
|
|
|
|
2020-08-10 11:07:20 +02:00
|
|
|
export function deleteTalerDatabase(idbFactory: IDBFactory): void {
|
2019-12-12 22:39:45 +01:00
|
|
|
Database.deleteDatabase(idbFactory, TALER_DB_NAME);
|
2019-12-19 13:48:37 +01:00
|
|
|
}
|