-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
/
Copy pathStorageManager.ts
177 lines (154 loc) · 5.82 KB
/
StorageManager.ts
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
/*
Copyright 2024 New Vector Ltd.
Copyright 2019-2021 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import { IndexedDBStore, IndexedDBCryptoStore } from "matrix-js-sdk/src/matrix";
import { logger } from "matrix-js-sdk/src/logger";
import { getIDBFactory } from "./StorageAccess";
const localStorage = window.localStorage;
// The JS SDK will add a prefix of "matrix-js-sdk:" to the sync store name.
const SYNC_STORE_NAME = "riot-web-sync";
const LEGACY_CRYPTO_STORE_NAME = "matrix-js-sdk:crypto";
const RUST_CRYPTO_STORE_NAME = "matrix-js-sdk::matrix-sdk-crypto";
function log(msg: string): void {
logger.log(`StorageManager: ${msg}`);
}
function error(msg: string, ...args: any[]): void {
logger.error(`StorageManager: ${msg}`, ...args);
}
export function tryPersistStorage(): void {
if (navigator.storage && navigator.storage.persist) {
navigator.storage.persist().then((persistent) => {
logger.log("StorageManager: Persistent?", persistent);
});
} else if (document.requestStorageAccess) {
// Safari
document.requestStorageAccess().then(
() => logger.log("StorageManager: Persistent?", true),
() => logger.log("StorageManager: Persistent?", false),
);
} else {
logger.log("StorageManager: Persistence unsupported");
}
}
export async function checkConsistency(): Promise<{
healthy: boolean;
cryptoInited: boolean;
dataInCryptoStore: boolean;
dataInLocalStorage: boolean;
}> {
log("Checking storage consistency");
log(`Local storage supported? ${!!localStorage}`);
log(`IndexedDB supported? ${!!getIDBFactory()}`);
let dataInLocalStorage = false;
let dataInCryptoStore = false;
let cryptoInited = false;
let healthy = true;
if (localStorage) {
dataInLocalStorage = localStorage.length > 0;
log(`Local storage contains data? ${dataInLocalStorage}`);
cryptoInited = !!localStorage.getItem("mx_crypto_initialised");
log(`Crypto initialised? ${cryptoInited}`);
} else {
healthy = false;
error("Local storage cannot be used on this browser");
}
if (getIDBFactory() && localStorage) {
const results = await checkSyncStore();
if (!results.healthy) {
healthy = false;
}
} else {
healthy = false;
error("Sync store cannot be used on this browser");
}
if (getIDBFactory()) {
const results = await checkCryptoStore();
dataInCryptoStore = results.exists;
if (!results.healthy) {
healthy = false;
}
} else {
healthy = false;
error("Crypto store cannot be used on this browser");
}
if (dataInLocalStorage && cryptoInited && !dataInCryptoStore) {
healthy = false;
error(
"Data exists in local storage and crypto is marked as initialised " +
" but no data found in crypto store. " +
"IndexedDB storage has likely been evicted by the browser!",
);
}
if (healthy) {
log("Storage consistency checks passed");
} else {
error("Storage consistency checks failed");
}
return {
dataInLocalStorage,
dataInCryptoStore,
cryptoInited,
healthy,
};
}
interface StoreCheck {
exists: boolean;
healthy: boolean;
}
async function checkSyncStore(): Promise<StoreCheck> {
let exists = false;
try {
exists = await IndexedDBStore.exists(getIDBFactory()!, SYNC_STORE_NAME);
log(`Sync store using IndexedDB contains data? ${exists}`);
return { exists, healthy: true };
} catch (e) {
error("Sync store using IndexedDB inaccessible", e);
}
log("Sync store using memory only");
return { exists, healthy: false };
}
async function checkCryptoStore(): Promise<StoreCheck> {
// check first if there is a rust crypto store
try {
const rustDbExists = await IndexedDBCryptoStore.exists(getIDBFactory()!, RUST_CRYPTO_STORE_NAME);
log(`Rust Crypto store using IndexedDB contains data? ${rustDbExists}`);
if (rustDbExists) {
// There was an existing rust database, so consider it healthy.
return { exists: true, healthy: true };
} else {
// No rust store, so let's check if there is a legacy store not yet migrated.
try {
const legacyIdbExists = await IndexedDBCryptoStore.existsAndIsNotMigrated(
getIDBFactory()!,
LEGACY_CRYPTO_STORE_NAME,
);
log(`Legacy Crypto store using IndexedDB contains non migrated data? ${legacyIdbExists}`);
return { exists: legacyIdbExists, healthy: true };
} catch (e) {
error("Legacy crypto store using IndexedDB inaccessible", e);
}
// No need to check local storage or memory as rust stack doesn't support them.
// Given that rust stack requires indexeddb, set healthy to false.
return { exists: false, healthy: false };
}
} catch (e) {
error("Rust crypto store using IndexedDB inaccessible", e);
return { exists: false, healthy: false };
}
}
/**
* Sets whether crypto has ever been successfully
* initialised on this client.
* StorageManager uses this to determine whether indexeddb
* has been wiped by the browser: this flag is saved to localStorage
* and if it is true and not crypto data is found, an error is
* presented to the user.
*
* @param {boolean} cryptoInited True if crypto has been set up
*/
export function setCryptoInitialised(cryptoInited: boolean): void {
localStorage.setItem("mx_crypto_initialised", String(cryptoInited));
}