add migration when backup is enabled

This commit is contained in:
Bruno Windels 2022-01-26 16:30:40 +01:00
parent 60ed276b8a
commit e80acd4d57
2 changed files with 40 additions and 14 deletions

View file

@ -6,6 +6,8 @@ import {addRoomToIdentity} from "../../e2ee/DeviceTracker.js";
import {SESSION_E2EE_KEY_PREFIX} from "../../e2ee/common.js"; import {SESSION_E2EE_KEY_PREFIX} from "../../e2ee/common.js";
import {SummaryData} from "../../room/RoomSummary"; import {SummaryData} from "../../room/RoomSummary";
import {RoomMemberStore, MemberData} from "./stores/RoomMemberStore"; import {RoomMemberStore, MemberData} from "./stores/RoomMemberStore";
import {encodeKey as encodeBackupKey} from "./stores/SessionNeedingBackupStore";
import {InboundGroupSessionStore, InboundGroupSessionEntry} from "./stores/InboundGroupSessionStore";
import {RoomStateEntry} from "./stores/RoomStateStore"; import {RoomStateEntry} from "./stores/RoomStateStore";
import {SessionStore} from "./stores/SessionStore"; import {SessionStore} from "./stores/SessionStore";
import {Store} from "./Store"; import {Store} from "./Store";
@ -39,6 +41,21 @@ export const schema: MigrationFunc[] = [
// TypeScript note: for now, do not bother introducing interfaces / alias // TypeScript note: for now, do not bother introducing interfaces / alias
// for old schemas. Just take them as `any`. // for old schemas. Just take them as `any`.
function createDatabaseNameHelper(db: IDBDatabase): ITransaction {
// the Store object gets passed in several things through the Transaction class (a wrapper around IDBTransaction),
// the only thing we should need here is the databaseName though, so we mock it out.
// ideally we should have an easier way to go from the idb primitive layer to the specific store classes where
// we implement logic, but for now we need this.
const databaseNameHelper: ITransaction = {
databaseName: db.name,
get idbFactory(): IDBFactory { throw new Error("unused");},
get IDBKeyRange(): typeof IDBKeyRange { throw new Error("unused");},
addWriteError() {},
};
return databaseNameHelper;
}
// how do we deal with schema updates vs existing data migration in a way that // how do we deal with schema updates vs existing data migration in a way that
//v1 //v1
function createInitialStores(db: IDBDatabase): void { function createInitialStores(db: IDBDatabase): void {
@ -223,17 +240,7 @@ async function changeSSSSKeyPrefix(db: IDBDatabase, txn: IDBTransaction) {
// v13 // v13
async function backupAndRestoreE2EEAccountToLocalStorage(db: IDBDatabase, txn: IDBTransaction, localStorage: IDOMStorage, log: ILogItem) { async function backupAndRestoreE2EEAccountToLocalStorage(db: IDBDatabase, txn: IDBTransaction, localStorage: IDOMStorage, log: ILogItem) {
const session = txn.objectStore("session"); const session = txn.objectStore("session");
// the Store object gets passed in several things through the Transaction class (a wrapper around IDBTransaction), const sessionStore = new SessionStore(new Store(session, createDatabaseNameHelper(db)), localStorage);
// the only thing we should need here is the databaseName though, so we mock it out.
// ideally we should have an easier way to go from the idb primitive layer to the specific store classes where
// we implement logic, but for now we need this.
const databaseNameHelper: ITransaction = {
databaseName: db.name,
get idbFactory(): IDBFactory { throw new Error("unused");},
get IDBKeyRange(): typeof IDBKeyRange { throw new Error("unused");},
addWriteError() {},
};
const sessionStore = new SessionStore(new Store(session, databaseNameHelper), localStorage);
// if we already have an e2ee identity, write a backup to local storage. // if we already have an e2ee identity, write a backup to local storage.
// further updates to e2ee keys in the session store will also write to local storage from 0.2.15 on, // further updates to e2ee keys in the session store will also write to local storage from 0.2.15 on,
// but here we make sure a backup is immediately created after installing the update and we don't wait until // but here we make sure a backup is immediately created after installing the update and we don't wait until
@ -273,6 +280,25 @@ async function clearAllStores(db: IDBDatabase, txn: IDBTransaction) {
} }
// v15 adds the sessionsNeedingBackup store, for key backup // v15 adds the sessionsNeedingBackup store, for key backup
function createSessionsNeedingBackup(db: IDBDatabase): void { async function createSessionsNeedingBackup(db: IDBDatabase, txn: IDBTransaction, localStorage: IDOMStorage, log: ILogItem): Promise<void> {
db.createObjectStore("sessionsNeedingBackup", {keyPath: "key"}); const backupStore = db.createObjectStore("sessionsNeedingBackup", {keyPath: "key"});
const session = txn.objectStore("session");
const ssssKey = await reqAsPromise(session.get(`${SESSION_E2EE_KEY_PREFIX}ssssKey`) as IDBRequest<string>);
const keyBackupEnabled = !!ssssKey;
log.set("key backup", keyBackupEnabled);
if (keyBackupEnabled) {
let count = 0;
try {
const inboundGroupSessions = txn.objectStore("inboundGroupSessions");
await iterateCursor<InboundGroupSessionEntry>(inboundGroupSessions.openCursor(), session => {
backupStore.add({key: encodeBackupKey(session.roomId, session.senderKey, session.sessionId)});
count += 1;
return NOT_DONE;
});
} catch (err) {
txn.abort();
log.log("could not migrate operations").catch(err);
}
log.set("count", count);
}
} }

View file

@ -26,7 +26,7 @@ type StorageEntry = {
key: string key: string
}; };
function encodeKey(roomId: string, senderKey: string, sessionId: string): string { export function encodeKey(roomId: string, senderKey: string, sessionId: string): string {
return `${roomId}|${senderKey}|${sessionId}`; return `${roomId}|${senderKey}|${sessionId}`;
} }