(feat): add cache package
(feat): improve local storage security (feat): move settings to dedicated settings package
This commit is contained in:
parent
fb095db7a6
commit
790a1db788
54 changed files with 1984 additions and 947 deletions
|
|
@ -423,6 +423,8 @@ export interface Storage extends SettingsStorageDefaults {
|
|||
legal_docs: z.infer<typeof legalDocsSchema>;
|
||||
cached_contacts: Contacts;
|
||||
cached_communities: Communities;
|
||||
cache_contacts: number;
|
||||
cache_messages_per_chat: number;
|
||||
omega_url: string;
|
||||
forced_omikron_url: string | undefined;
|
||||
forced_omikron_public_key: string | undefined;
|
||||
|
|
@ -475,6 +477,8 @@ export const storageDefaults: Storage = {
|
|||
},
|
||||
cached_contacts: [],
|
||||
cached_communities: [],
|
||||
cache_contacts: 5,
|
||||
cache_messages_per_chat: 20,
|
||||
omega_url: "https://omega.tensamin.net",
|
||||
forced_omikron_url: undefined,
|
||||
forced_omikron_public_key: undefined,
|
||||
|
|
|
|||
|
|
@ -38,6 +38,13 @@ type ElectronDesktopApi = {
|
|||
iconDataUrl?: string;
|
||||
}) => Promise<void>;
|
||||
};
|
||||
secureStorage?: {
|
||||
getStatus?: () => Promise<{ available: boolean; backend: string | null }>;
|
||||
load?: (key: string) => Promise<string | null>;
|
||||
save?: (key: string, value: string) => Promise<void>;
|
||||
delete?: (key: string) => Promise<void>;
|
||||
clear?: () => Promise<void>;
|
||||
};
|
||||
};
|
||||
|
||||
declare global {
|
||||
|
|
|
|||
98
packages/shared/src/indexedDb.ts
Normal file
98
packages/shared/src/indexedDb.ts
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
export const TENSAMIN_DB_NAME = "tensamin";
|
||||
export const TENSAMIN_DB_VERSION = 1;
|
||||
export type TensaminStore = "storage" | "cache" | "keys";
|
||||
|
||||
const stores: TensaminStore[] = ["storage", "cache", "keys"];
|
||||
let databasePromise: Promise<IDBDatabase> | undefined;
|
||||
|
||||
export function openTensaminDatabase(
|
||||
indexedDb: IDBFactory = globalThis.indexedDB,
|
||||
) {
|
||||
databasePromise ??= new Promise<IDBDatabase>((resolve, reject) => {
|
||||
const request = indexedDb.open(TENSAMIN_DB_NAME, TENSAMIN_DB_VERSION);
|
||||
request.onupgradeneeded = () => {
|
||||
for (const store of stores) {
|
||||
if (!request.result.objectStoreNames.contains(store)) {
|
||||
request.result.createObjectStore(store);
|
||||
}
|
||||
}
|
||||
};
|
||||
request.onsuccess = () => {
|
||||
request.result.onversionchange = () => request.result.close();
|
||||
resolve(request.result);
|
||||
};
|
||||
request.onerror = () => {
|
||||
databasePromise = undefined;
|
||||
reject(request.error);
|
||||
};
|
||||
request.onblocked = () => {
|
||||
databasePromise = undefined;
|
||||
reject(new Error("The Tensamin database upgrade is blocked."));
|
||||
};
|
||||
});
|
||||
return databasePromise;
|
||||
}
|
||||
|
||||
export async function getDatabaseEntry<T>(store: TensaminStore, key: string) {
|
||||
const database = await openTensaminDatabase();
|
||||
return new Promise<T | undefined>((resolve, reject) => {
|
||||
const request = database
|
||||
.transaction(store, "readonly")
|
||||
.objectStore(store)
|
||||
.get(key);
|
||||
request.onsuccess = () => resolve(request.result as T | undefined);
|
||||
request.onerror = () => reject(request.error);
|
||||
});
|
||||
}
|
||||
|
||||
export async function setDatabaseEntry(
|
||||
store: TensaminStore,
|
||||
key: string,
|
||||
value: unknown,
|
||||
) {
|
||||
const database = await openTensaminDatabase();
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const transaction = database.transaction(store, "readwrite");
|
||||
transaction.objectStore(store).put(value, key);
|
||||
transaction.oncomplete = () => resolve();
|
||||
transaction.onerror = () => reject(transaction.error);
|
||||
transaction.onabort = () => reject(transaction.error);
|
||||
});
|
||||
}
|
||||
|
||||
export async function deleteDatabaseEntry(store: TensaminStore, key: string) {
|
||||
const database = await openTensaminDatabase();
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const transaction = database.transaction(store, "readwrite");
|
||||
transaction.objectStore(store).delete(key);
|
||||
transaction.oncomplete = () => resolve();
|
||||
transaction.onerror = () => reject(transaction.error);
|
||||
transaction.onabort = () => reject(transaction.error);
|
||||
});
|
||||
}
|
||||
|
||||
export async function listDatabaseEntries(
|
||||
store: TensaminStore,
|
||||
keyPrefix: string,
|
||||
) {
|
||||
const database = await openTensaminDatabase();
|
||||
return new Promise<Array<[string, unknown]>>((resolve, reject) => {
|
||||
const entries: Array<[string, unknown]> = [];
|
||||
const cursor = database
|
||||
.transaction(store, "readonly")
|
||||
.objectStore(store)
|
||||
.openCursor();
|
||||
cursor.onsuccess = () => {
|
||||
const value = cursor.result;
|
||||
if (!value) {
|
||||
resolve(entries);
|
||||
return;
|
||||
}
|
||||
if (typeof value.key === "string" && value.key.startsWith(keyPrefix)) {
|
||||
entries.push([value.key, value.value]);
|
||||
}
|
||||
value.continue();
|
||||
};
|
||||
cursor.onerror = () => reject(cursor.error);
|
||||
});
|
||||
}
|
||||
|
|
@ -46,6 +46,7 @@ const settings = {
|
|||
},
|
||||
},
|
||||
application: {
|
||||
cache: {},
|
||||
theme: {},
|
||||
licenses: {},
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in a new issue