diff --git a/main/preload.ts b/main/preload.ts index f9a7ce8dd..bad47d1a5 100644 --- a/main/preload.ts +++ b/main/preload.ts @@ -12,6 +12,7 @@ import { logError } from './utils/logging'; import { ElectronFile } from './types'; import { getEncryptionKey, setEncryptionKey } from './utils/safeStorage'; import { clearElectronStore } from './utils/electronStore'; +import { openLocalCache } from './utils/cache'; // Patch the global WebSocket constructor to use the correct DevServer url const fixHotReloadNext12 = () => { @@ -193,4 +194,5 @@ windowObject['ElectronAPIs'] = { getEncryptionKey, setEncryptionKey, clearElectronStore, + openLocalCache, }; diff --git a/main/utils/cache.ts b/main/utils/cache.ts new file mode 100644 index 000000000..4a9d46871 --- /dev/null +++ b/main/utils/cache.ts @@ -0,0 +1,50 @@ +import { ipcRenderer } from 'electron/renderer'; +import path from 'path'; +import { readFile, writeFile, existsSync, mkdir } from 'promise-fs'; +import crypto from 'crypto'; + +const CACHE_DIR = 'ente'; + +const getCacheDir = async () => { + const systemCacheDir = await ipcRenderer.invoke('get-path', 'cache'); + return path.join(systemCacheDir, CACHE_DIR); +}; + +export async function openLocalCache(cacheName: string) { + const cacheDir = await getCacheDir(); + const cacheBucketDir = path.join(cacheDir, cacheName); + if (!existsSync(cacheBucketDir)) { + await mkdir(cacheBucketDir, { recursive: true }); + } + return new DiskCache(cacheBucketDir); +} + +class DiskCache { + constructor(private cacheBucketDir: string) {} + + async put(cacheKey: string, response: Response): Promise { + const cachePath = getAssetCachePath(this.cacheBucketDir, cacheKey); + await writeFile( + cachePath, + new Uint8Array(await response.arrayBuffer()) + ); + } + + async match(cacheKey: string): Promise { + const cachePath = getAssetCachePath(this.cacheBucketDir, cacheKey); + if (existsSync(cachePath)) { + return new Response(await readFile(cachePath)); + } else { + return undefined; + } + } +} + +function getAssetCachePath(cacheDir: string, cacheKey: string) { + // hashing the key to prevent illegal filenames + const cacheKeyHash = crypto + .createHash('sha256') + .update(cacheKey) + .digest('hex'); + return path.join(cacheDir, cacheKeyHash); +} diff --git a/main/utils/ipcComms.ts b/main/utils/ipcComms.ts index 999e768ad..97e59f8d5 100644 --- a/main/utils/ipcComms.ts +++ b/main/utils/ipcComms.ts @@ -5,6 +5,7 @@ import { Tray, Notification, safeStorage, + app, } from 'electron'; import { createWindow } from './createWindow'; import { buildContextMenu } from './menuUtil'; @@ -82,4 +83,8 @@ export default function setupIpcComs( ipcMain.handle('safeStorage-decrypt', (_, message) => { return safeStorage.decryptString(message); }); + + ipcMain.handle('get-path', (_, message) => { + return app.getPath(message); + }); }