mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-02-24 04:00:59 +01:00
web: fix storage for Firefox & Tor private mode
This commit is contained in:
@@ -42,7 +42,7 @@ async function initializeDatabase(persistence: DatabasePersistence) {
|
||||
});
|
||||
|
||||
database.setup(
|
||||
new NNStorage("Notesnook", persistence),
|
||||
await NNStorage.createInstance("Notesnook", persistence),
|
||||
EventSource,
|
||||
FS,
|
||||
new Compressor()
|
||||
|
||||
@@ -28,8 +28,6 @@ import { initalizeLogger, logger } from "./utils/logger";
|
||||
import { AuthProps } from "./views/auth";
|
||||
import { loadDatabase } from "./hooks/use-database";
|
||||
|
||||
initalizeLogger();
|
||||
|
||||
type Route<TProps = null> = {
|
||||
component: () => Promise<{
|
||||
default: TProps extends null
|
||||
@@ -150,6 +148,7 @@ function isSessionExpired(path: Routes): RouteWithPath<AuthProps> | null {
|
||||
renderApp();
|
||||
|
||||
async function renderApp() {
|
||||
await initalizeLogger();
|
||||
const {
|
||||
path,
|
||||
route: { component, props }
|
||||
|
||||
@@ -174,29 +174,25 @@ export type UseStore = <T>(
|
||||
export class IndexedDBKVStore implements IKVStore {
|
||||
store: UseStore;
|
||||
|
||||
static isIndexedDBSupported(): boolean {
|
||||
return "indexedDB" in window;
|
||||
static async isIndexedDBSupported(): Promise<boolean> {
|
||||
if (!("indexedDB" in window)) return false;
|
||||
try {
|
||||
await promisifyIDBRequest(indexedDB.open("checkIDBSupport"));
|
||||
return true;
|
||||
} catch {
|
||||
console.error("IndexedDB is not supported in this browser.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
constructor(databaseName: string, storeName: string) {
|
||||
this.store = this.createStore(databaseName, storeName);
|
||||
}
|
||||
|
||||
private promisifyRequest<T = undefined>(
|
||||
request: IDBRequest<T> | IDBTransaction
|
||||
): Promise<T> {
|
||||
return new Promise<T>((resolve, reject) => {
|
||||
// @ts-ignore - file size hacks
|
||||
request.oncomplete = request.onsuccess = () => resolve(request.result);
|
||||
// @ts-ignore - file size hacks
|
||||
request.onabort = request.onerror = () => reject(request.error);
|
||||
});
|
||||
}
|
||||
|
||||
private createStore(dbName: string, storeName: string): UseStore {
|
||||
const request = indexedDB.open(dbName);
|
||||
request.onupgradeneeded = () => request.result.createObjectStore(storeName);
|
||||
const dbp = this.promisifyRequest(request);
|
||||
const dbp = promisifyIDBRequest(request);
|
||||
|
||||
return (txMode, callback) =>
|
||||
dbp.then((db) =>
|
||||
@@ -213,26 +209,26 @@ export class IndexedDBKVStore implements IKVStore {
|
||||
callback(this.result);
|
||||
this.result.continue();
|
||||
};
|
||||
return this.promisifyRequest(store.transaction);
|
||||
return promisifyIDBRequest(store.transaction);
|
||||
}
|
||||
|
||||
get<T>(key: string): Promise<T | undefined> {
|
||||
return this.store("readonly", (store) =>
|
||||
this.promisifyRequest(store.get(key))
|
||||
promisifyIDBRequest(store.get(key))
|
||||
);
|
||||
}
|
||||
|
||||
set(key: string, value: any): Promise<void> {
|
||||
return this.store("readwrite", (store) => {
|
||||
store.put(value, key);
|
||||
return this.promisifyRequest(store.transaction);
|
||||
return promisifyIDBRequest(store.transaction);
|
||||
});
|
||||
}
|
||||
|
||||
setMany(entries: [string, any][]): Promise<void> {
|
||||
return this.store("readwrite", (store) => {
|
||||
entries.forEach((entry) => store.put(entry[1], entry[0]));
|
||||
return this.promisifyRequest(store.transaction);
|
||||
return promisifyIDBRequest(store.transaction);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -241,7 +237,7 @@ export class IndexedDBKVStore implements IKVStore {
|
||||
Promise.all(
|
||||
keys.map(async (key) => [
|
||||
key,
|
||||
await this.promisifyRequest(store.get(key))
|
||||
await promisifyIDBRequest(store.get(key))
|
||||
])
|
||||
)
|
||||
);
|
||||
@@ -250,21 +246,21 @@ export class IndexedDBKVStore implements IKVStore {
|
||||
delete(key: string): Promise<void> {
|
||||
return this.store("readwrite", (store) => {
|
||||
store.delete(key);
|
||||
return this.promisifyRequest(store.transaction);
|
||||
return promisifyIDBRequest(store.transaction);
|
||||
});
|
||||
}
|
||||
|
||||
deleteMany(keys: string[]): Promise<void> {
|
||||
return this.store("readwrite", (store: IDBObjectStore) => {
|
||||
keys.forEach((key: IDBValidKey) => store.delete(key));
|
||||
return this.promisifyRequest(store.transaction);
|
||||
return promisifyIDBRequest(store.transaction);
|
||||
});
|
||||
}
|
||||
|
||||
clear(): Promise<void> {
|
||||
return this.store("readwrite", (store) => {
|
||||
store.clear();
|
||||
return this.promisifyRequest(store.transaction);
|
||||
return promisifyIDBRequest(store.transaction);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -272,7 +268,7 @@ export class IndexedDBKVStore implements IKVStore {
|
||||
return this.store("readonly", (store) => {
|
||||
// Fast path for modern browsers
|
||||
if (store.getAllKeys) {
|
||||
return this.promisifyRequest(
|
||||
return promisifyIDBRequest(
|
||||
store.getAllKeys() as unknown as IDBRequest<KeyType[]>
|
||||
);
|
||||
}
|
||||
@@ -289,7 +285,7 @@ export class IndexedDBKVStore implements IKVStore {
|
||||
return this.store("readonly", (store) => {
|
||||
// Fast path for modern browsers
|
||||
if (store.getAll) {
|
||||
return this.promisifyRequest(store.getAll() as IDBRequest<T[]>);
|
||||
return promisifyIDBRequest(store.getAll() as IDBRequest<T[]>);
|
||||
}
|
||||
|
||||
const items: T[] = [];
|
||||
@@ -308,10 +304,10 @@ export class IndexedDBKVStore implements IKVStore {
|
||||
// (although, hopefully we'll get a simpler path some day)
|
||||
if (store.getAll && store.getAllKeys) {
|
||||
return Promise.all([
|
||||
this.promisifyRequest(
|
||||
promisifyIDBRequest(
|
||||
store.getAllKeys() as unknown as IDBRequest<KeyType[]>
|
||||
),
|
||||
this.promisifyRequest(store.getAll() as IDBRequest<ValueType[]>)
|
||||
promisifyIDBRequest(store.getAll() as IDBRequest<ValueType[]>)
|
||||
]).then(([keys, values]) => keys.map((key, i) => [key, values[i]]));
|
||||
}
|
||||
|
||||
@@ -325,3 +321,16 @@ export class IndexedDBKVStore implements IKVStore {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function promisifyIDBRequest<T = undefined>(
|
||||
request: IDBRequest<T> | IDBTransaction
|
||||
): Promise<T> {
|
||||
return new Promise<T>((resolve, reject) => {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore - file size hacks
|
||||
request.oncomplete = request.onsuccess = () => resolve(request.result);
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore - file size hacks
|
||||
request.onabort = request.onerror = () => reject(request.error);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -31,17 +31,21 @@ export type DatabasePersistence = "memory" | "db";
|
||||
|
||||
const APP_SALT = "oVzKtazBo7d8sb7TBvY9jw";
|
||||
|
||||
|
||||
export class NNStorage {
|
||||
database: IKVStore;
|
||||
database!: IKVStore;
|
||||
|
||||
constructor(name: string, persistence: DatabasePersistence = "db") {
|
||||
this.database =
|
||||
static async createInstance(
|
||||
name: string,
|
||||
persistence: DatabasePersistence = "db"
|
||||
) {
|
||||
const storage = new NNStorage();
|
||||
storage.database =
|
||||
persistence === "memory"
|
||||
? new MemoryKVStore()
|
||||
: IndexedDBKVStore.isIndexedDBSupported()
|
||||
: (await IndexedDBKVStore.isIndexedDBSupported())
|
||||
? new IndexedDBKVStore(name, "keyvaluepairs")
|
||||
: new LocalStorageKVStore();
|
||||
return storage;
|
||||
}
|
||||
|
||||
read<T>(key: string): Promise<T | undefined> {
|
||||
@@ -78,7 +82,7 @@ export class NNStorage {
|
||||
const keyData = await crypto.exportKey(password, salt);
|
||||
|
||||
if (
|
||||
IndexedDBKVStore.isIndexedDBSupported() &&
|
||||
(await IndexedDBKVStore.isIndexedDBSupported()) &&
|
||||
window?.crypto?.subtle &&
|
||||
keyData.key
|
||||
) {
|
||||
@@ -94,7 +98,10 @@ export class NNStorage {
|
||||
}
|
||||
|
||||
async getCryptoKey(name: string): Promise<string | undefined> {
|
||||
if (IndexedDBKVStore.isIndexedDBSupported() && window?.crypto?.subtle) {
|
||||
if (
|
||||
(await IndexedDBKVStore.isIndexedDBSupported()) &&
|
||||
window?.crypto?.subtle
|
||||
) {
|
||||
const pbkdfKey = await this.read<CryptoKey>(name);
|
||||
const cipheredKey = await this.read<EncryptedKey | string>(`${name}@_k`);
|
||||
if (typeof cipheredKey === "string") return cipheredKey;
|
||||
|
||||
@@ -28,8 +28,8 @@ import { DatabasePersistence, NNStorage } from "../interfaces/storage";
|
||||
import { zip } from "./zip";
|
||||
|
||||
let logger: typeof _logger;
|
||||
function initalizeLogger(persistence: DatabasePersistence = "db") {
|
||||
initalize(new NNStorage("Logs", persistence));
|
||||
async function initalizeLogger(persistence: DatabasePersistence = "db") {
|
||||
initalize(await NNStorage.createInstance("Logs", persistence));
|
||||
logger = _logger.scope("notesnook-web");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user